@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
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago