@luffalab/luffa-evm-sdk
v1.0.7
Published
luffa evm ts sdk
Readme
Luffa EVM SDK
Installation
npm install @luffalab/luffa-evm-sdkQuick Start
Basic Initialization
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 standard for multi-wallet discovery, which is the recommended way to obtain wallet objects, superior to directly using window.ethereum.
How It Works
- SDK triggers
eip6963:announceProviderevent during initialization - When the page emits
eip6963:requestProviderevent, SDK triggerseip6963:announceProviderevent again - DApps can listen to
eip6963:announceProviderevent to obtain wallet objects
Usage Example
// 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.
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.
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.
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.
// Switch to BSC mainnet
await luffaProvider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x38' }]
});
console.log('Switch successful')Parameters:
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)
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.
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.
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
// 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
// Listen for network changes
luffaProvider.on('chainChanged', (chainId) => {
console.log('Network switched to:', chainId);
});Remove Event Listeners
// 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?
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?
// 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?
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?
// 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.
