# Luffa EVM SDK

## Installation

```bash
npm install @luffalab/luffa-evm-sdk
```

## Quick Start

### Basic Initialization

```typescript
import { LuffaEvmSdk } from '@luffalab/luffa-evm-sdk';

// Initialize SDK
const sdk = new LuffaEvmSdk({
  network: 'eth' // Supported: 'eth', 'eth_sepolia', 'bsc', 'bsc_test'
});

// Check if running in Luffa wallet environment
import { isLuffa } from '@luffalab/luffa-evm-sdk';

if (isLuffa()) {
  console.log('Running in Luffa wallet');
}
```

## EIP-6963 Multi-Wallet Discovery

Luffa EVM SDK implements the [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) standard for multi-wallet discovery, which is the recommended way to obtain wallet objects, superior to directly using `window.ethereum`.

### How It Works

1. SDK triggers `eip6963:announceProvider` event during initialization
2. When the page emits `eip6963:requestProvider` event, SDK triggers `eip6963:announceProvider` event again
3. DApps can listen to `eip6963:announceProvider` event to obtain wallet objects

### Usage Example

```typescript
// Define received wallet provider type
interface EIP6963ProviderDetail {
  info: {
    uuid: string;
    name: string;
    icon: string;
    rdns: string;
  };
  provider: any; // Actually EIP-1193 compatible provider
}

// Store discovered wallet providers
const discoveredProviders: EIP6963ProviderDetail[] = [];
let luffaProvider = null; // The actual provider to use

// Listen for wallet provider announcements
window.addEventListener('eip6963:announceProvider', (event: CustomEvent<EIP6963ProviderDetail>) => {
  const { detail } = event;

  // Check if provider with same UUID already exists
  const exists = discoveredProviders.some(p => p.info.uuid === detail.info.uuid);
  if (!exists) {
    discoveredProviders.push(detail);

    // If this is Luffa wallet, store the provider
    if (detail.info.name === 'LuffaEvmWallet') {
      luffaProvider = detail.provider;
    }

    console.log(`Wallet discovered: ${detail.info.name}`);
  }
});

// Request wallet providers
window.dispatchEvent(new Event('eip6963:requestProvider'));
```

We strongly recommend DApp developers use the EIP-6963 standard to detect and connect wallets, rather than directly relying on the `window.ethereum` object.

## API Reference

### Wallet Connection Methods

#### `eth_requestAccounts`

Request user to connect wallet and return authorized account addresses.

```typescript
const accounts = await luffaProvider.request({
  method: 'eth_requestAccounts'
});

console.log('Account address:', accounts[0]);
```

#### `eth_accounts`

Get currently connected account addresses without triggering user authorization popup.

```typescript
const accounts = await luffaProvider.request({
  method: 'eth_accounts'
});

if (accounts.length > 0) {
  console.log('Currently connected account:', accounts[0]);
} else {
  console.log('No accounts connected');
}
```

### Network Related Methods

#### `eth_chainId`

Get the current connected blockchain network ID.

```typescript
const chainId = await luffaProvider.request({
  method: 'eth_chainId'
});

console.log('Current network ID:', chainId); // e.g.: "0x1" (Ethereum mainnet)
```

**Supported Networks:**

| Network Name | Network ID | Chain ID (Hex) | Chain ID (Decimal) |
|--------------|------------|----------------|-------------------|
| Ethereum Mainnet | `eth` | `0x1` | 1 |
| Ethereum Sepolia Testnet | `eth_sepolia` | `0xaa36a7` | 11155111 |
| Binance Smart Chain Mainnet | `bsc` | `0x38` | 56 |
| Binance Smart Chain Testnet | `bsc_test` | `0x61` | 97 |

#### `wallet_switchEthereumChain`

Request to switch to a specified blockchain network.

```typescript
// Switch to BSC mainnet
await luffaProvider.request({
  method: 'wallet_switchEthereumChain',
  params: [{ chainId: '0x38' }]
});
console.log('Switch successful')
```

**Parameters:**
```typescript
interface SwitchChainParams {
  chainId: string; // Chain ID in hexadecimal format
}
```

### Transaction Related Methods

#### `eth_sendTransaction`

Send Ethereum transactions. **Note: Current version only supports smart contract calls, does not support regular ETH transfers.**

##### Using ethers.js to Construct Contract Calls (Recommended)

```typescript
import { ethers } from 'ethers';

// ERC20 token transfer example
async function sendERC20Token() {
  // ERC20 contract ABI (only transfer method needed)
  const erc20ABI = [
    "function transfer(address to, uint256 amount) returns (bool)"
  ];
  
  const tokenAddress = '0xA0b86a33E6441e6e80D0c4C34F4f5FD4F4f5FD4F'; // Token contract address
  const recipientAddress = '0x742d35Cc6634C0532925a3b8D0C9e3e0C8b0e8c8';
  const amount = ethers.parseUnits('100', 18); // 100 tokens (18 decimals)
  
  // Create contract interface
  const contract = new ethers.Interface(erc20ABI);
  
  // Encode function call data
  const data = contract.encodeFunctionData('transfer', [recipientAddress, amount]);
  
  // Send transaction
  const txHash = await window.ethereum.request({
    method: 'eth_sendTransaction',
    params: [{
      to: tokenAddress,
      data: data,
      gas: '0x186a0', // 100000
      gasPrice: '0x09184e72a000' // 10 gwei
    }]
  });
  
  console.log('ERC20 transfer transaction hash:', txHash);
  return txHash;
}

// ERC20 approval example
async function approveERC20Token() {
  const erc20ABI = [
    "function approve(address spender, uint256 amount) returns (bool)"
  ];
  
  const tokenAddress = '0xA0b86a33E6441e6e80D0c4C34F4f5FD4F4f5FD4F';
  const spenderAddress = '0x1234567890123456789012345678901234567890'; // Authorized address
  const amount = ethers.parseUnits('1000', 18); // Approve 1000 tokens
  
  const contract = new ethers.Interface(erc20ABI);
  const data = contract.encodeFunctionData('approve', [spenderAddress, amount]);
  
  const txHash = await window.ethereum.request({
    method: 'eth_sendTransaction',
    params: [{
      to: tokenAddress,
      data: data,
      gas: '0xea60', // 60000
      gasPrice: '0x09184e72a000'
    }]
  });
  
  console.log('ERC20 approval transaction hash:', txHash);
  return txHash;
}

// Call custom contract method
async function callCustomContract() {
  // Custom contract ABI
  const customABI = [
    "function setData(string memory _data, uint256 _value) payable"
  ];
  
  const contractAddress = '0xYourContractAddress';
  const contract = new ethers.Interface(customABI);
  
  // Encode function call
  const data = contract.encodeFunctionData('setData', [
    'Hello World',
    ethers.parseUnits('42', 0) // Integer 42
  ]);
  
  const txHash = await window.ethereum.request({
    method: 'eth_sendTransaction',
    params: [{
      to: contractAddress,
      data: data,
      value: '0x16345785d8a0000', // 0.1 ETH (if function is payable)
      gas: '0x30d40', // 200000
      gasPrice: '0x09184e72a000'
    }]
  });
  
  console.log('Custom contract call transaction hash:', txHash);
  return txHash;
}
```

#### `personal_sign`

Sign messages for personal use, commonly used for identity verification.

```typescript
const message = 'Hello, Luffa Wallet!';
const signature = await luffaProvider.request({
  method: 'personal_sign',
  params: [message]
});

console.log('Signature result:', signature);
```

### Disconnect

#### `wallet_revokePermissions`

Disconnect from the wallet.

```typescript
await luffaProvider.request({
  method: 'wallet_revokePermissions'
});
console.log('Disconnected');
```

## Event Listening

SDK supports listening to wallet state change events, consistent with MetaMask.

### Account Change Events

```typescript
// Listen for account changes
luffaProvider.on('accountsChanged', (accounts) => {
  console.log('Accounts changed:', accounts);
  if (accounts.length === 0) {
    console.log('User disconnected');
  } else {
    console.log('Current account:', accounts[0]);
  }
});
```

### Network Change Events

```typescript
// Listen for network changes
luffaProvider.on('chainChanged', (chainId) => {
  console.log('Network switched to:', chainId);
});
```

### Remove Event Listeners

```typescript
// Remove specific listener
const handleAccountsChanged = (accounts) => {
  console.log('Account change:', accounts);
};

luffaProvider.on('accountsChanged', handleAccountsChanged);
luffaProvider.removeListener('accountsChanged', handleAccountsChanged);
```

## FAQ

### Q: How to detect if user is in Luffa wallet?

```typescript
import { isLuffa } from '@luffalab/luffa-evm-sdk';

if (isLuffa()) {
  // In Luffa wallet
} else {
  // Not in Luffa wallet, show guide page
}
```

### Q: How to handle network switching?

```typescript
// Listen for network changes
luffaProvider.on('chainChanged', (chainId) => {
  // Network switched, update application state
  window.location.reload(); // Simple handling approach
});

// Actively switch network
try {
  await luffaProvider.request({
    method: 'wallet_switchEthereumChain',
    params: [{ chainId: '0x38' }] // BSC mainnet
  });
} catch (error) {
  if (error.code === 4001) {
    console.log('User rejected network switch');
  } else if (error.code === -32602) {
    console.log('Invalid chain ID');
  } else {
    console.log('Network switch failed:', error.message);
  }
}
```

### Q: How to handle user disconnection?

```typescript
luffaProvider.on('accountsChanged', (accounts) => {
  if (accounts.length === 0) {
    // User disconnected
    // Clean application state, redirect to connection page
    localStorage.removeItem('userAccount');
    window.location.href = '/connect';
  }
});
```

### Q: How to verify signatures?

```typescript
// Frontend signing
const message = 'Hello World';
const signature = await luffaProvider.request({
  method: 'personal_sign',
  params: [message]
});

// Backend verification (Node.js example)
const ethers = require('ethers');

function verifySignature(message, signature, expectedAddress) {
  const recoveredAddress = ethers.utils.verifyMessage(message, signature);
  return recoveredAddress.toLowerCase() === expectedAddress.toLowerCase();
}
```

## Support

For questions or suggestions, please contact the Luffa team or submit an Issue on GitHub.
