Selendra

Documentation

Migration Guide: From Ethers.js to Selendra SDK

Guide for developers transitioning from Ethers.js to Selendra SDK.

Quick Reference

Ethers.jsSelendra 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.providers with SelendraSDK
  • Replace ethers.Contract with Contract from SDK
  • Replace ethers.utils.parseEther() with sdk.parseBalance()
  • Replace ethers.utils.formatEther() with sdk.formatBalance()
  • Replace manual event listeners with useEventListener hook

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.

Contribute

Found an issue or want to contribute?

Help us improve this documentation by editing this page on GitHub.

Edit this page on GitHub