Skip to main content

EVM Integration

Overview

The Node.js SDK provides seamless integration with Ethereum Virtual Machine (EVM) compatible blockchains including Ethereum, Polygon, Binance Smart Chain, Arbitrum, Optimism, and more.

Supported EVM Chains

BlockchainChain IDRPC Endpoint
Ethereum Mainnet0x1https://rpc.ankr.com/eth
Ethereum Goerli0x5https://rpc.ankr.com/eth_goerli
Polygon Mainnet0x89https://rpc.ankr.com/polygon
Polygon Mumbai0x80001https://rpc.ankr.com/polygon_mumbai
BSC Mainnet0x38https://bsc-dataseed.binance.org
BSC Testnet0x61https://data-seed-prebsc-1-s1.binance.org:8545
Arbitrum One0xa4b1https://arb1.arbitrum.io/rpc
Optimism0xahttps://mainnet.optimism.io

Integration with Ethers.js

Basic Setup

const { Web3Auth } = require('@web3auth/node-sdk')
const { ethers } = require('ethers')

// Initialize Web3Auth
const web3auth = new Web3Auth({
clientId: 'YOUR_CLIENT_ID',
web3AuthNetwork: 'sapphire_mainnet',
})

await web3auth.init()

// Authenticate user
const provider = await web3auth.connect({
verifier: 'my-custom-verifier',
verifierId: 'user@example.com',
idToken: 'JWT_TOKEN',
chainId: '0x1', // Ethereum mainnet
})

// Get private key and create wallet
const privateKey = await provider.request({ method: 'eth_private_key' })
const wallet = new ethers.Wallet(privateKey)

// Connect to RPC provider
const rpcProvider = new ethers.providers.JsonRpcProvider('https://rpc.ankr.com/eth')
const connectedWallet = wallet.connect(rpcProvider)

console.log('Wallet Address:', await connectedWallet.getAddress())

Send Transaction

async function sendTransaction(toAddress, amount) {
try {
// Create transaction
const tx = {
to: toAddress,
value: ethers.utils.parseEther(amount),
gasLimit: 21000,
}

// Send transaction
const txResponse = await connectedWallet.sendTransaction(tx)
console.log('Transaction Hash:', txResponse.hash)

// Wait for confirmation
const receipt = await txResponse.wait()
console.log('Transaction confirmed in block:', receipt.blockNumber)

return receipt
} catch (error) {
console.error('Transaction failed:', error.message)
throw error
}
}

// Usage
await sendTransaction('0x742d35Cc6635C0532925a3b8138341B0F7E8a4e8', '0.1')

Smart Contract Interaction

// ERC-20 Token ABI (simplified)
const erc20Abi = [
'function balanceOf(address owner) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)',
'function approve(address spender, uint256 amount) returns (bool)',
'function symbol() view returns (string)',
'function decimals() view returns (uint8)',
]

async function interactWithERC20(tokenAddress) {
// Create contract instance
const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, connectedWallet)

// Get token info
const symbol = await tokenContract.symbol()
const decimals = await tokenContract.decimals()
console.log(`Token: ${symbol}, Decimals: ${decimals}`)

// Get balance
const balance = await tokenContract.balanceOf(connectedWallet.address)
console.log('Token Balance:', ethers.utils.formatUnits(balance, decimals))

// Transfer tokens
const transferTx = await tokenContract.transfer(
'0x742d35Cc6635C0532925a3b8138341B0F7E8a4e8',
ethers.utils.parseUnits('10', decimals)
)

await transferTx.wait()
console.log('Transfer completed:', transferTx.hash)
}

// Usage with USDC on Ethereum
await interactWithERC20('0xA0b86a33E6441E51DBF5c4dF02a7b29fAdab0215')

Integration with Viem

Basic Setup

const { createWalletClient, createPublicClient, http } = require('viem')
const { privateKeyToAccount } = require('viem/accounts')
const { mainnet, polygon } = require('viem/chains')

// Get private key from Web3Auth
const privateKey = await provider.request({ method: 'eth_private_key' })

// Create account
const account = privateKeyToAccount(privateKey)

// Create wallet client
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http('https://rpc.ankr.com/eth'),
})

// Create public client for reading
const publicClient = createPublicClient({
chain: mainnet,
transport: http('https://rpc.ankr.com/eth'),
})

console.log('Account Address:', account.address)

Send Transaction with Viem

const { parseEther, formatEther } = require('viem')

async function sendTransactionViem(toAddress, amount) {
try {
// Get current balance
const balance = await publicClient.getBalance({
address: account.address,
})
console.log('Current Balance:', formatEther(balance), 'ETH')

// Send transaction
const hash = await walletClient.sendTransaction({
to: toAddress,
value: parseEther(amount),
})

console.log('Transaction Hash:', hash)

// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log('Transaction confirmed in block:', receipt.blockNumber)

return receipt
} catch (error) {
console.error('Transaction failed:', error.message)
throw error
}
}

Smart Contract with Viem

const { getContract, parseUnits, formatUnits } = require('viem')

// ERC-20 ABI
const erc20Abi = [
{
inputs: [{ name: 'owner', type: 'address' }],
name: 'balanceOf',
outputs: [{ name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
name: 'transfer',
outputs: [{ name: '', type: 'bool' }],
stateMutability: 'nonpayable',
type: 'function',
},
] as const

async function erc20WithViem(tokenAddress) {
// Create contract instance
const contract = getContract({
address: tokenAddress,
abi: erc20Abi,
publicClient,
walletClient,
})

// Read balance
const balance = await contract.read.balanceOf([account.address])
console.log('Token Balance:', formatUnits(balance, 18))

// Write operation
const hash = await contract.write.transfer([
'0x742d35Cc6635C0532925a3b8138341B0F7E8a4e8',
parseUnits('10', 18),
])

const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log('Transfer completed:', receipt.transactionHash)
}

Multi-Chain Support

Chain Switching

async function connectToChain(chainId, authParams) {
// Connect to specific chain (stateless)
const provider = await web3auth.connect({
...authParams,
chainId,
})

return provider
}

// Connect to Polygon
const polygonProvider = await connectToChain('0x89', {
verifier: 'my-custom-verifier',
verifierId: 'user@example.com',
idToken: 'JWT_TOKEN',
})

Multi-Chain Wallet Class

class MultiChainEVMWallet {
constructor(clientId) {
this.web3auth = new Web3Auth({
clientId,
web3AuthNetwork: 'sapphire_mainnet',
})
this.wallets = new Map()
}

async initialize() {
await this.web3auth.init()
}

async connectToChain(chainId, chainConfig) {
// Connect to specific chain
const provider = await this.web3auth.connect({
verifier: 'my-custom-verifier',
verifierId: 'user@example.com',
idToken: 'JWT_TOKEN',
chainId,
})

// Create wallet for this chain
const privateKey = await provider.request({ method: 'eth_private_key' })
const wallet = new ethers.Wallet(privateKey)
const connectedWallet = wallet.connect(new ethers.providers.JsonRpcProvider(chainConfig.rpcUrl))

this.wallets.set(chainId, {
provider,
wallet: connectedWallet,
config: chainConfig,
})

return connectedWallet
}

getWallet(chainId) {
const walletData = this.wallets.get(chainId)
return walletData ? walletData.wallet : null
}

async getBalances() {
const balances = {}

for (const [chainId, walletData] of this.wallets) {
const balance = await walletData.wallet.getBalance()
balances[chainId] = {
balance: ethers.utils.formatEther(balance),
address: walletData.wallet.address,
network: walletData.config.name,
}
}

return balances
}
}

// Usage
const multiWallet = new MultiChainEVMWallet('YOUR_CLIENT_ID')
await multiWallet.initialize()

// Connect to multiple chains
await multiWallet.connectToChain('0x1', {
name: 'Ethereum',
rpcUrl: 'https://rpc.ankr.com/eth',
})

await multiWallet.connectToChain('0x89', {
name: 'Polygon',
rpcUrl: 'https://rpc.ankr.com/polygon',
})

const balances = await multiWallet.getBalances()
console.log('Multi-chain balances:', balances)

Gas Management

Estimate Gas

async function estimateAndSendTransaction(toAddress, amount) {
const tx = {
to: toAddress,
value: ethers.utils.parseEther(amount),
}

// Estimate gas
const gasEstimate = await connectedWallet.estimateGas(tx)
console.log('Estimated Gas:', gasEstimate.toString())

// Get gas price
const gasPrice = await connectedWallet.getGasPrice()
console.log('Gas Price:', ethers.utils.formatUnits(gasPrice, 'gwei'), 'gwei')

// Add gas settings to transaction
tx.gasLimit = gasEstimate.mul(120).div(100) // Add 20% buffer
tx.gasPrice = gasPrice

// Send transaction
const txResponse = await connectedWallet.sendTransaction(tx)
return txResponse
}

EIP-1559 (Type 2) Transactions

async function sendEIP1559Transaction(toAddress, amount) {
// Get fee data
const feeData = await connectedWallet.getFeeData()

const tx = {
to: toAddress,
value: ethers.utils.parseEther(amount),
type: 2, // EIP-1559
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
}

const txResponse = await connectedWallet.sendTransaction(tx)
return txResponse
}

Error Handling

async function robustEVMTransaction(toAddress, amount) {
try {
// Check balance first
const balance = await connectedWallet.getBalance()
const amountWei = ethers.utils.parseEther(amount)

if (balance.lt(amountWei)) {
throw new Error('Insufficient balance')
}

// Send transaction with retries
let retries = 3
while (retries > 0) {
try {
const tx = await sendTransaction(toAddress, amount)
return tx
} catch (error) {
retries--

if (error.code === 'NONCE_EXPIRED' && retries > 0) {
console.log('Nonce expired, retrying...')
continue
}

throw error
}
}
} catch (error) {
console.error('EVM transaction failed:', error.message)
throw error
}
}

Next Steps