@evmlord/multicall-sdk v0.0.4
Multicall SDK
A TypeScript / JavaScript SDK for interacting with a deployed Multicall3 (V3) contract via ethers-v6. Batch together on-chain calls into a single eth_call, decode results, handle failures, and retrieve block info — all with one simple class.
Features
- Batch calls (
aggregate,tryAggregate,aggregate3,aggregate3Value,blockAndAggregate,tryBlockAndAggregate) - View helpers: 
getBlockNumber,getBlockHash,getLastBlockHash,getCurrentBlockTimestamp,getCurrentBlockGasLimit,getCurrentBlockCoinbase,getEthBalance,getBasefee,getChainId - Native 
bigintreturn values (noBigNumberoverhead) - Fully typed with TypeScript (declarations included)
 - Single entry-point: import all constants, ABIs and types from 
@evmlord/multicall-sdk 
Installation
# via yarn
yarn add @evmlord/multicall-sdk
# or npm
npm install @evmlord/multicall-sdkUsage
Constructor
- With 
chainId 
import { ethers } from "ethers";
import {
  Multicall,
  MulticallRawResult,
  Call,
  Call3,
  Call3Value,
  MulticallConstructorArgs,
} from "@evmlord/multicall-sdk";
import erc20Abi from "./abi/erc20.json";
const rpc = "https://your-rpc-here.org/";
// 1) Create an ethers provider (e.g. Infura, Alchemy, JSON-RPC, etc.)
const provider = new ethers.JsonRpcProvider("https://rpc.ankr.com/eth");
const multicall = new Multicall({
  provider // your ethers provider
  chainId: ChainId.MAINNET, // from exported ChainId enum
});- With custom Multicall address
 
import { ethers } from "ethers";
import {
  Multicall,
  MulticallRawResult,
  Call,
  Call3,
  Call3Value,
  MulticallConstructorArgs,
} from "@evmlord/multicall-sdk";
import erc20Abi from "./abi/erc20.json";
// 1) Create an ethers provider (e.g. Infura, Alchemy, JSON-RPC, etc.)
const provider = new ethers.JsonRpcProvider("https://rpc.ankr.com/eth");
// 2) Instantiate the SDK
const multicall = new Multicall({
  provider, // your ethers provider
  multicallAddress: "The address of the deployed multicall3 contract", // override default if deployed elsewhere
});Aggregating
// 3) Prepare one or more Calls
const token = new ethers.Contract("0x…ERC20", erc20Abi, provider);
const calls: Call[] = [
  { contract: token, functionFragment: "balanceOf", args: ["0xYourAddress1"] },
  { contract: token, functionFragment: "balanceOf", args: ["0xYourAddress2"] },
  { contract: token, functionFragment: "balanceOf", args: ["0xYourAddress3"] },
  { contract: token, functionFragment: "totalSupply", args: [] },
];
// 4) Batch them
const { blockNumber, returnData } = await multicall.aggregate(calls);
// returnData is string[] of raw hex
// To decode:
console.log(
  "Balance of Address 1:",
  token.interface.decodeFunctionResult("balanceOf", returnData[0])[0]
);
console.log(
  "Balance of Address 2:",
  token.interface.decodeFunctionResult("balanceOf", returnData[1])[0]
);
console.log(
  "Balance of Address 3:",
  token.interface.decodeFunctionResult("balanceOf", returnData[2])[0]
);
console.log(
  "TotalSupply:",
  token.interface.decodeFunctionResult("totalSupply", returnData[4])[0]
);
console.log("At block:", blockNumber);Batch Methods
aggregate(calls: Call[])Reverts on any failure.
const { blockNumber, returnData } = await multicall.aggregate(calls);tryAggregate(requireSuccess: boolean, calls: Call[])Continue past failed calls; returns
Array<[success, decodedOrRaw]>.tryBlockAndAggregate(requireSuccess: boolean, calls: Call[])Like
tryAggregate, plus returns{ blockNumber, blockHash, returnData }.blockAndAggregate(calls: Call[])Alias for
tryBlockAndAggregate(true, calls).aggregate3(calls: Call3[])Allow individual failures via
allowFailureflag on each call.aggregate3Value(calls: Call3Value[])Like
aggregate3, but each call can send ETH (value) and you supply the totalmsg.value.
// ── aggregate3 example ─────────────────────────────────────────
// call two view methods in one batch, but let one of them fail
const calls3: Call3[] = [
  {
    contract:        token,
    functionFragment:'balanceOf',
    args:            [ user ],
    allowFailure:    true
  },
  {
    contract:        token,
    functionFragment:'nonExistedProperty', // this will throw
    args:            [],
    allowFailure:    true
  }
]
const results3 = await multicall.aggregate3(calls3)
// results3 is Array<[success: boolean, data]>
console.log('balanceOf →', results3[0])
console.log('nonExistedProperty →', results3[1])
// ── aggregate3Value example ────────────────────────────────────
// imagine a payable helper that charges a small fee per call
const helper = new ethers.Contract(
  "0x…EHelperContract",   // your helper address
  HelperABI,                 //  your ABI
  provider
)
// send 0.001 ETH with each call to fetch some on-chain data
const fee  = 0.001n * 10n**18n  // 0.001 ETH in wei as bigint
const calls3Value: Call3Value[] = [
  {
    contract:        helper,
    functionFragment:'getSomeData',
    args:            [ user ],
    allowFailure:    false,
    value:           fee
  },
  {
    contract:        helper,
    functionFragment:'getOtherData',
    args:            [ user, 42 ],
    allowFailure:    true,
    value:           fee
  }
]
const results3Value = await multicall.aggregate3Value(calls3Value)
// returns Array<[success: boolean, data]>
console.log('getSomeData →', results3Value[0])
console.log('getOtherData →', results3Value[1])What’s happening here?
aggregate3You passallowFailure: trueon any call that might revert—failed calls return[false, rawHex]while successes decode normally.aggregate3ValueEach call can carry its own ETH payment (value), and the SDK automatically sums them into onemsg.valuefor the batch. Any call markedallowFailure: truewon’t abort the entire batch if it reverts.
Helper Functions
All return either Promise<bigint> or Promise<string>:
getEthBalanceGets the ETH balance of an addressconst ethBalance = await multicall.getEthBalance("address");getBlockHashGets the block hashOnly works for 256 most recent, excluding current according to Solidity docs
const blockHash = await multicall.getBlockHash(blockNumber);getLastBlockHashGets the last blocks hashconst lastBlockHash = await multicall.getLastBlockHash();getCurrentBlockTimestampGets the current block timestampconst currentBlockTimestamp = await multicall.getCurrentBlockTimestamp();getCurrentBlockDifficultyGets the current block difficultyconst currentBlockDifficulty = await multicall.getCurrentBlockDifficulty();getCurrentBlockGasLimitGets the current block gas limitconst currentBlockGasLimit = await multicall.getCurrentBlockGasLimit();getCurrentBlockCoinbaseGets the current block coinbaseconst currentBlockCoinbase = await multicall.getCurrentBlockCoinbase();
Testing
This SDK ships with a comprehensive Mocha + Chai + Sinon test suite.
# run tests
yarn testContributing
- Fork & clone
 yarn install- Write code under 
src/ - Add tests under 
test/ - Run 
yarn test - Submit a PR
 
License
MIT License - Copyright (©) 2025 EVMlord
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago