Migration Guide: From Ethers.js to Selendra SDK
Guide for developers transitioning from Ethers.js to Selendra SDK.
Quick Reference
| Ethers.js | Selendra SDK |
|---|---|
new ethers.providers.WebSocketProvider() | new SelendraSDK() |
provider.getBalance(address) | sdk.getBalance(address) |
contract.callStatic.method() | contract.call(method) |
contract.method(...args) | contract.send(method, options, ...args) |
wallet.sendTransaction(tx) | sdk.sendTransaction(options) |
Core Concepts
Provider, Connection & Accounts
Ethers.js
import { ethers } from "ethers";
const provider = new ethers.providers.WebSocketProvider(
"wss://mainnet.infura.io"
);
const signer = provider.getSigner();
// Create wallet
const wallet = ethers.Wallet.fromMnemonic(mnemonic);
Selendra SDK
import { SelendraSDK } from "@selendrajs/sdk";
const sdk = new SelendraSDK({
network: "mainnet",
wsEndpoint: "wss://rpc.selendra.org",
autoConnect: true,
});
// Create account (both Substrate and EVM)
const account = await sdk.importAccountFromMnemonic(mnemonic, {
type: "both",
});
Transaction Operations
Basic Transfers
Ethers.js
const tx = await signer.sendTransaction({
to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
value: ethers.utils.parseEther("1.0"),
});
await tx.wait();
Selendra SDK
const tx = await sdk.transfer({
to: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
amount: BigInt("1000000000000"), // 1 SEL with 12 decimals
memo: "Payment",
});
console.log("Hash:", tx.hash, "Status:", tx.status);
Smart Contract Interaction
Ethers.js
const contract = new ethers.Contract(address, abi, signer);
// Read
const balance = await contract.balanceOf(address);
console.log(ethers.utils.formatUnits(balance, 18));
// Write
const tx = await contract.transfer(recipient, ethers.utils.parseEther("1.0"));
await tx.wait();
Selendra SDK
const contract = new Contract(address, abi, sdk);
// Read
const balance = await contract.call("balanceOf", address);
console.log(sdk.formatBalance(balance, 18));
// Write
const tx = await contract.send(
"transfer",
{ gasLimit: BigInt("100000") },
recipient,
BigInt("1000000000000000000")
);
Event Handling
Ethers.js
contract.on("Transfer", (from, to, amount, event) => {
console.log(
`Transfer of ${ethers.utils.formatUnits(amount)} from ${from} to ${to}`
);
});
// Query past events
const filter = contract.filters.Transfer(from, to);
const events = await contract.queryFilter(filter, fromBlock, toBlock);
Selendra SDK
import { useEventListener } from "@selendrajs/sdk/react";
const { events } = useEventListener({
contract,
eventName: "Transfer",
fromBlock: "latest",
});
// Programmatic
const unsubscribe = await contract.on("Transfer", (from, to, amount) => {
console.log(`Transfer of ${amount.toString()} from ${from} to ${to}`);
});
// Query past events
const events = await contract.getEvents("Transfer", {
fromBlock: 12345,
toBlock: "latest",
filters: { from, to },
});
Utility Functions
| Operation | Ethers.js | Selendra SDK |
| ---------------- | ---------------------------------- | --------------------------------------------------- | ----- | ---------- |
| Parse units | ethers.utils.parseEther("1.5") | sdk.parseBalance("1.5", 12) |
| Format units | ethers.utils.formatEther(amount) | sdk.formatBalance(amount, 12) |
| Validate address | ethers.utils.isAddress(address) | sdk.validateAddress(address) returns 'substrate' | 'evm' | 'invalid' |
Advanced Patterns
Gas Estimation & Batch Transactions
Ethers.js
// Gas estimation
const gasEstimate = await contract.estimateGas.transfer(recipient, amount);
const gasPrice = await provider.getGasPrice();
// Using Multicall
const multicall = new ethers.Contract(multicallAddress, multicallAbi, signer);
const calls = [
[
contract1Address,
contract1.interface.encodeFunctionData("balanceOf", [addr1]),
],
[
contract2Address,
contract2.interface.encodeFunctionData("balanceOf", [addr2]),
],
];
const [, results] = await multicall.aggregate(calls);
Selendra SDK
// Gas estimation
const gasEstimate = await sdk.estimateGas({
to: contractAddress,
data: contract.encodeFunctionData("transfer", [recipient, amount]),
});
// Built-in batch support
const results = await Promise.all([
contract1.call("balanceOf", addr1),
contract2.call("balanceOf", addr2),
]);
Contract Deployment
Ethers.js
const factory = new ethers.ContractFactory(abi, bytecode, signer);
const contract = await factory.deploy(constructorArg1, constructorArg2);
await contract.deployed();
Selendra SDK
const deployed = await sdk.deployContract({
bytecode,
abi,
constructorArgs: [constructorArg1, constructorArg2],
from: account.address,
gasLimit: BigInt("2000000"),
});
console.log("Contract deployed at:", deployed.address);
React Integration
Ethers.js
import { ethers } from "ethers";
import { useEffect, useState } from "react";
function useBalance(address) {
const [balance, setBalance] = useState(null);
useEffect(() => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const getBalance = async () => {
const bal = await provider.getBalance(address);
setBalance(ethers.utils.formatEther(bal));
};
getBalance();
}, [address]);
return balance;
}
Selendra SDK
import { useBalance } from "@selendrajs/sdk/react";
function BalanceDisplay({ address }) {
const { balance, loading, error } = useBalance({ address });
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
Balance: {balance?.free} {balance?.tokenSymbol}
</div>
);
}
Migration Checklist
Easy Replacements
- Replace
ethers.providerswithSelendraSDK - Replace
ethers.ContractwithContractfrom SDK - Replace
ethers.utils.parseEther()withsdk.parseBalance() - Replace
ethers.utils.formatEther()withsdk.formatBalance() - Replace manual event listeners with
useEventListenerhook
Adaptation Required
- Update transaction handling to Selendra format
- Adapt contract interaction patterns
- Update gas estimation logic
- Replace Web3 provider connections
- Update error handling
New Opportunities
- Explore unified Substrate + EVM accounts
- Leverage built-in caching and state management
- Use React hooks for blockchain operations
- Implement cross-chain functionality
Complete Migration Example
Before (Ethers.js)
import { ethers } from "ethers";
import { useState, useEffect } from "react";
function TokenTransfer() {
const [contract, setContract] = useState(null);
const [balance, setBalance] = useState("0");
useEffect(() => {
const init = async () => {
const web3Provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = await web3Provider.getSigner();
const tokenContract = new ethers.Contract(
TOKEN_ADDRESS,
TOKEN_ABI,
signer
);
setContract(tokenContract);
const bal = await tokenContract.balanceOf(await signer.getAddress());
setBalance(ethers.utils.formatUnits(bal, 18));
};
init();
}, []);
const handleTransfer = async (to, amount) => {
const tx = await contract.transfer(to, ethers.utils.parseUnits(amount, 18));
await tx.wait();
const bal = await contract.balanceOf(await signer.getAddress());
setBalance(ethers.utils.formatUnits(bal, 18));
};
return (
<div>
<p>Balance: {balance}</p>
<TransferForm onTransfer={handleTransfer} />
</div>
);
}
After (Selendra SDK)
import { useAccount, useBalance, useContract } from "@selendrajs/sdk/react";
function TokenTransfer() {
const { account } = useAccount();
const { balance, refetch } = useBalance();
const { contract, send } = useContract({
address: TOKEN_ADDRESS,
abi: TOKEN_ABI,
});
const handleTransfer = async (to: string, amount: string) => {
await send("transfer", { from: account?.address }, to, BigInt(amount));
await refetch();
};
return (
<div>
<p>
Balance: {balance?.free} {balance?.tokenSymbol}
</p>
<TransferForm onTransfer={handleTransfer} />
</div>
);
}
Resources
Need help? Open an issue or start a discussion.
Found an issue or want to contribute?
Help us improve this documentation by editing this page on GitHub.
