Git Product home page Git Product logo

ethereum-multicall's Introduction

npm version downloads

ethereum-multicall

ethereum-multicall is a lightweight library for interacting with the Multicall3 smart contract.

Multicall allows multiple smart contract constant function calls to be grouped into a single call and the results aggregated into a single result. This reduces the number of separate JSON RPC requests that need to be sent over the network if using a remote node like Infura, and provides the guarantee that all values returned are from the same block. The latest block number is also returned along with the aggregated results.

ethereum-multicall is fully written in typescript so has full compile time support. The motivation of this package was to expose a super simple and easy to understand interface for you to take the full benefits of the multicalls. Also to not being opinionated on how you use it, you can use it with web3, ethers or even pass in a custom nodeUrl and we do it for you. This package takes care of the decoding for you but at the same time if you dont want it to you can turn that part off.

Supports

The below networks are supported by default, and custom networks can be supported by providing your own instance of a deployed Multicall contract.

Chain Chain ID
Mainnet 1
Ropsten 3
Rinkeby 4
Görli 5
Optimism 10
Kovan 42
Matic 137
KovanOptimism 69
xDai 100
xDaiTestnet 10200
GoerliOptimism 420
SepoliaOptimism 11155420
Arbitrum 42161
RinkebyArbitrum 421611
GoerliArbitrum 421613
SepoliaArbitrum 421614
Mumbai 80001
Sepolia 11155111
AvalancheMainnet 43114
AvalancheFuji 43113
FantomTestnet 4002
Fantom 250
BSC 56
BSC_Testnet 97
Moonbeam 1284
Moonriver 1285
MoonbaseAlphaTestnet 1287
Harmony 1666600000
Cronos 25
Fuse 122
SongbirdCanaryNetwork 19
CostonTestnet 16
Boba 288
Aurora 1313161554
Astar 592
OKC 66
Heco 128
Metis 1088
RSK 30
RSKTestnet 31
Evmos 9001
EvmosTestnet 9000
Thundercore 108
ThundercoreTestnet 18
Oasis 26863
Celo 42220
Godwoken 71402
GodwokenTestnet 71401
Klatyn 8217
Milkomeda 2001
KCC 321
Etherlite 111
Linea Testnet 59140
Linea 59144
Scroll 534352
Scroll Sepolia 534531
zkSync Era 324
zkSync Era Testnet 280
zkSync Era Sepolia 300
Shibarium 109
Mantle 5000
Mantle Testnet 5001
Base 8453
Base Testnet 84531
Blast Sepolia 168587773
PolygonZkEvm 1101
PolygonZkEvmTestnet 1442
Zora 7777777
Zora Testnet 999
Flare Mainnet 14
Pulsechain Mainnet 369
Starknet 300
Starknet Testnet 301
Blast 81457
Amoy 80002
Mode 34443
Mode Testnet 919
Manta Pacific Mainnet 169

Installation

npm:

$ npm install ethereum-multicall

yarn:

$ yarn add ethereum-multicall

Usage

Overloaded methods

As the official docs mentions here:

Due to signature overloading, multiple functions can have the same name. The first function specified in the ABI will be bound to its name. To access overloaded functions, use the full typed signature of the functions (e.g. contract["foobar(address,uint256)"]).

So, when creating the contract call context, under the calls array property we should have that in mind and use the method signature rather than the method name. E.g.

const contractCallContext: ContractCallContext = {
  reference: 'upV2Controller',
  contractAddress: '0x19891DdF6F393C02E484D7a942d4BF8C0dB1d001',
  abi: [
    {
      inputs: [],
      name: 'getVirtualPrice',
      outputs: [
        {
          internalType: 'uint256',
          name: '',
          type: 'uint256',
        },
      ],
      stateMutability: 'view',
      type: 'function',
    },
    {
      inputs: [
        {
          internalType: 'uint256',
          name: 'sentValue',
          type: 'uint256',
        },
      ],
      name: 'getVirtualPrice',
      outputs: [
        {
          internalType: 'uint256',
          name: '',
          type: 'uint256',
        },
      ],
      stateMutability: 'view',
      type: 'function',
    },
  ],
  calls: [
    {
      reference: 'getVirtualPriceWithInput',
      methodName: 'getVirtualPrice(uint256)',
      methodParameters: ['0xFFFFFFFFFFFFF'],
    },
    {
      reference: 'getVirtualPriceWithoutInput',
      methodName: 'getVirtualPrice()',
      methodParameters: [],
    },
  ],
};

Import examples:

JavaScript (ES3)

var ethereumMulticall = require('ethereum-multicall');

JavaScript (ES5 or ES6)

const ethereumMulticall = require('ethereum-multicall');

JavaScript (ES6) / TypeScript

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';

ethers usage example

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';
import { ethers } from 'ethers';

let provider = ethers.getDefaultProvider();

// you can use any ethers provider context here this example is
// just shows passing in a default provider, ethers hold providers in
// other context like wallet, signer etc all can be passed in as well.
const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true });

const contractCallContext: ContractCallContext[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
    },
    {
        reference: 'testContract2',
        contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
        abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256', name: "path", "type": "address[]" }] } ],
        calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

// results:
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
            calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      },
      testContract2: {
          originalContractCallContext:  {
            reference: 'testContract2',
            contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
            abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256[]' ] } ],
            calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: [BigNumber, BigNumber, BigNumber] }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      }
  },
  blockNumber: 10994677
}

web3 usage example

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';
import Web3 from 'web3';

const web3 = new Web3('https://some.local-or-remote.node:8546');

const multicall = new Multicall({ web3Instance: web3, tryAggregate: true });

const contractCallContext: ContractCallContext[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
    },
    {
        reference: 'testContract2',
        contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
        abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256', name: "path", "type": "address[]" }] } ],
        calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

// results:
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
            calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      },
      testContract2: {
          originalContractCallContext:  {
            reference: 'testContract2',
            contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
            abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256[]' ] } ],
            calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: [BigNumber, BigNumber, BigNumber] }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      }
  },
  blockNumber: 10994677
}

specify call block number

The multicall instance call method has an optional second argument of type ContractCallOptions.

One of the options is the blockNumber, so you can choose the height where you want the data from.

It is compatible with both ethers and web3 providers.

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';
import Web3 from 'web3';

const web3 = new Web3('https://some.local-or-remote.node:8546');

const multicall = new Multicall({ web3Instance: web3, tryAggregate: true });

const contractCallContext: ContractCallContext[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext,{
    blockNumber: '14571050'
});
console.log(results);

// results: it will have the same block as requested
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
            calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      },
  },
  blockNumber: 14571050
}

passing extra context to the call

If you want to store any context or state so you don't need to look back over arrays once you got the result back. it can be stored in context within ContractCallContext.

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';
import { ethers } from 'ethers';

let provider = ethers.getDefaultProvider();

// you can use any ethers provider context here this example is
// just shows passing in a default provider, ethers hold providers in
// other context like wallet, signer etc all can be passed in as well.
const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true });

// this is showing you having the same context for all `ContractCallContext` but you can also make this have
// different context for each `ContractCallContext`, as `ContractCallContext<TContext>` takes generic `TContext`.
const contractCallContext: ContractCallContext<{extraContext: string, foo4: boolean}>[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }],
        // pass it in here!
        context: {
          extraContext: 'extraContext',
          foo4: true
        }
    },
    {
        reference: 'testContract2',
        contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
        abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256', name: "path", "type": "address[]" }] } ],
        calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }],
         // pass it in here!
        context: {
          extraContext: 'extraContext2',
          foo4: false
        }
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

// results:
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
            calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }],
            context: {
                extraContext: 'extraContext',
                foo4: true
            }
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      },
      testContract2: {
          originalContractCallContext:  {
            reference: 'testContract2',
            contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
            abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256[]' ] } ],
            calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }],
            context: {
                extraContext: 'extraContext2',
                foo4: false
            }
          },
          callsReturnContext: [{
              returnValues: [{ amounts: [BigNumber, BigNumber, BigNumber] }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      }
  },
  blockNumber: 10994677
}

try aggregate

By default if you dont turn tryAggregate to true if 1 eth_call fails in your multicall the whole result will throw. If you turn tryAggregate to true it means if 1 of your eth_call methods fail it still return you the rest of the results. It will still be in the same order as you expect but you have a success boolean to check if it passed or failed. Keep in mind that if using a custom multicall contract deployment, Multicall version 1's will not work. Use a Multicall2 deployment (contract can be found here).

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';

const multicall = new Multicall({ nodeUrl: 'https://some.local-or-remote.node:8546', tryAggregate: true });

const contractCallContext: ContractCallContext[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] }, { name: 'foo_fail', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }, { reference: 'fooCall_fail', methodName: 'foo_fail', methodParameters: [42] }]
    },
    {
        reference: 'testContract2',
        contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
        abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256', name: "path", "type": "address[]" }] } ],
        calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

// results:
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] }, { name: 'foo_fail', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
             calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }, { reference: 'fooCall_fail', methodName: 'foo_fail', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          },
          {
              returnValues: [],
              decoded: false,
              reference: 'fooCall_fail',
              methodName: 'foo_fail',
              methodParameters: [42],
              success: false
          }]
      },
      testContract2: {
          originalContractCallContext:  {
            reference: 'testContract2',
            contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
            abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256[]' ] } ],
            calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: [BigNumber, BigNumber, BigNumber] }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      }
  },
  blockNumber: 10994677
}

custom jsonrpc provider usage example

import {
  Multicall,
  ContractCallResults,
  ContractCallContext,
} from 'ethereum-multicall';

const multicall = new Multicall({ nodeUrl: 'https://some.local-or-remote.node:8546', tryAggregate: true });

const contractCallContext: ContractCallContext[] = [
    {
        reference: 'testContract',
        contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
        abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
        calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
    },
    {
        reference: 'testContract2',
        contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
        abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256', name: "path", "type": "address[]" }] } ],
        calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

// results:
{
  results: {
      testContract: {
          originalContractCallContext:  {
            reference: 'testContract',
            contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
            abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
            calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: BigNumber }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      },
      testContract2: {
          originalContractCallContext:  {
            reference: 'testContract2',
            contractAddress: '0x66BF8e2E890eA0392e158e77C6381b34E0771318',
            abi: [ { name: 'fooTwo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256[]' ] } ],
            calls: [{ reference: 'fooTwoCall', methodName: 'fooTwo', methodParameters: [42] }]
          },
          callsReturnContext: [{
              returnValues: [{ amounts: [BigNumber, BigNumber, BigNumber] }],
              decoded: true,
              reference: 'fooCall',
              methodName: 'foo',
              methodParameters: [42],
              success: true
          }]
      }
  },
  blockNumber: 10994677
}

Multicall contracts

by default it looks at your network from the provider you passed in and makes the contract address to the known multicall contract addresses 0xcA11bde05977b3631167028862bE2a173976CA11 this is deployed on every single network but etherlite which uses 0x21681750D7ddCB8d1240eD47338dC984f94AF2aC.

If you wanted this to point at a different multicall contract address just pass that in the options when creating the multicall instance, example:

```ts
const multicall = new Multicall({
  multicallCustomContractAddress: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
  // your rest of your config depending on the provider your using.
});

Issues

Please raise any issues in the below link.

https://github.com/joshstevens19/ethereum-multicall/issues

ethereum-multicall's People

Contributors

akaryatrh avatar andhovesyan avatar asafy avatar boivlad avatar croll83 avatar cuttleman avatar damianmarti avatar dannote avatar dr-maxwell avatar dzaptech avatar eliobricenov avatar hadiesna avatar isabellewei avatar joshstevens19 avatar jsertx avatar krivochenko avatar mds1 avatar michecode avatar ncookiez avatar omahs avatar omurovec avatar razgraf avatar whatl3y avatar xuefeng-zhu avatar yohox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ethereum-multicall's Issues

Uncaught (in promise) Error: invalid address (argument="address", value=0, code=INVALID_ARGUMENT, version=address/5.7.0) (argument=null, value=0, code=INVALID_ARGUMENT, version=abi/5.7.0)

Hello,

First of all thank you for putting together ethereum-multicall for us, really appreciate it.

I am running into an issue on my react js code when trying to call multicall.
The issue looks like this:

Uncaught (in promise) Error: invalid address (argument="address", value=0, code=INVALID_ARGUMENT, version=address/5.7.0) (argument=null, value=0, code=INVALID_ARGUMENT, version=abi/5.7.0)

I have double checked the addresses and they are correct and they worked when not trying to use multiCall.

The following is my code on how I am calling multiCall

async loadDataUsingMulticall() {
    const { web3 } = this.state;
    var multicall = null;
    console.log("This is the mirror address:",MIRROR_ADDRESS);


    multicall = new Multicall({
      multicallCustomContractAddress: MIRROR_ADDRESS,
      nodeUrl: BSC_RPC_URL,
      web3Instance: web3,
      tryAggregate: true,
    });

    console.log("test zero");

    const contractCallContext = [
      {
        reference: "mirrorBalance",
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [
          {
            methodName: "balanceOf",
            methodParameters: [this.state.account[0]],
          },
        ],
      },
      {
        reference: "morWholeBurn",
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [
          {
            methodName: "balanceOf",
            methodParameters: ["0x0000000000000000000000000000000000000000"],
          },
        ],
      },
      {
        reference: "morRFIBurn",
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [
          {
            methodName: "balanceOf",
            methodParameters: ["0x000000000000000000000000000000000000dead"],
          },
        ],
      },
      {
        reference: "totalFees",
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [{ methodName: "totalFees", methodParameters: [] }],
      },
      {
        reference: "numFarmers",
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [{ methodName: "numFarmers", methodParameters: [] }],
      },
    ];

    console.log("test before");
    console.log(MIRROR_ADDRESS);

    // Loop for dynamic method calls like 'farmers' and 'farmingLeaderboard'
    for (let i = 0; i < 5; i++) {
      contractCallContext.push({
        reference: `farmer${i}`,
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [{ methodName: "farmers", methodParameters: [i] }],
      });
      contractCallContext.push({
        reference: `farmer${i}amt`,
        contractAddress: MIRROR_ADDRESS,
        abi: MIRRORABI,
        calls: [{ methodName: "farmingLeaderboard", methodParameters: [i] }],
      });
    }
    ////////////

    const callResults = await multicall.call(contractCallContext);
    console.log("test)");
    console.log(callResults);

    
    // Accessing individual results
    const mirrorBalance = Web3.utils.fromWei(
      callResults.results.mirrorBalance.callsReturnContext[0].returnValues[0],
      "ether",
    );
    const morWholeBurn = Web3.utils.fromWei(
      callResults.results.morWholeBurn.callsReturnContext[0].returnValues[0],
      "ether",
    );
    const morRFIBurn = Web3.utils.fromWei(
      callResults.results.morRFIBurn.callsReturnContext[0].returnValues[0],
      "ether",
    );
    const totalFees = Web3.utils.fromWei(
      callResults.results.totalFees.callsReturnContext[0].returnValues[0],
      "ether",
    );
    const numFarmers = parseInt(
      callResults.results.numFarmers.callsReturnContext[0].returnValues[0],
    );
    console.log("This is mirror Balance");
    console.log(mirrorBalance);
    console.log("This is morWhole Burn");
    console.log(morWholeBurn);
    console.log("This is total Farmers");
    console.log(numFarmers);
    // Loop to access farmers and farmingLeaderboard data
    let farmerData = {};
    for (let i = 0; i < 5; i++) {
      farmerData[`farmer${i}`] =
        callResults.results[`farmer${i}`].callsReturnContext[0].returnValues[0];
      farmerData[`farmer${i}amt`] =
        callResults.results[
          `farmer${i}amt`
        ].callsReturnContext[0].returnValues[0];
    }

    // Update state with all the gathered data
    this.setState({
      mirrorBalance,
      morWholeBurn,
      morRFIBurn,
      totalFees,
      numFarmers,
      ...farmerData,
      loadingLeaderBoard: false,
    });
  }

Polygon zkEvm Mainnet blocNumber issue

Screenshot_1
Hello, hello, it's me again. :/ For some reason, whenever I request balances for Polygon zkEvm byBlockNumber using multicall, it always returns block+1 for the given block. Here's the script so you can debug it. Also, how i can provide chainId to my request to avoid unneeded request get_chainId? As you can see im using "0xca11bde05977b3631167028862be2a173976ca11" for mutlicall smart contract address and "https://rpc.ankr.com/polygon_zkevm" for provider.

import {
Multicall,
ContractCallResults,
ContractCallContext,
} from "ethereum-multicall";

import { NetworkCoinToken } from "../models/models";

export const GetFinalResultv2 = async () => {
let tokens: any[] = [
{
id: 2452,
name: "ChainLink Token",
shortName: "LINK",
contractAddress: "0x4b16e4752711a7abec32799c976f3cefc0111f2b",
networkId: 40,
},
{
id: 2465,
name: "Ankr Staked ETH",
contractAddress: "0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c",
networkId: 40,
},
];
const walletAddress = "0xA9D1e08C7793af67e9d92fe308d5697FB81d3E43";
const contractCallContext: ContractCallContext[] = tokens.map(
(token: NetworkCoinToken) => ({
reference: token.contractAddress || "",
context: token.name,
contractAddress: token.contractAddress || "",
abi: [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [
{
name: "_owner",
type: "address",
},
],
outputs: [
{
name: "balance",
type: "uint256",
},
],
},
],
calls: [
{
reference: token.contractAddress || "",
methodName: "balanceOf",
methodParameters: [walletAddress || ""],
},
],
})
);
const multicall = new Multicall({
tryAggregate: true,
multicallCustomContractAddress:
"0xca11bde05977b3631167028862be2a173976ca11",
nodeUrl: "https://rpc.ankr.com/polygon_zkevm",
});
const getData = async () => {
let resultData: ContractCallResults | null = null;
try {
const numberBl = "10275514";
resultData = await multicall.call(contractCallContext, {
blockNumber: numberBl,
});

		if (resultData) {
			console.log("Provided block number", numberBl);
			console.log(resultData);
			return resultData;
		}
	} catch (error: any) {
		console.log(error);
	}
};
const finalData: any = (await getData()) || [];
return finalData;

};

Add python usage examples

Nice work! Thanks for sharing with everyone. Any chance you could add python usage examples to the list of languages on the main page? Please consider that for the py family. Thanks again.

multicall.call result success is true but no tx is sent to blockchain

Hi there, I´m playing around with multicalls at my hardhat local environment.

I deployed this Multicall3 https://etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code
the only change I did was moving from version 0.8.12 to 0.8.17

This is my multicall object creation:

const multicall = new ethereumMulticall.Multicall({ 
            ethersProvider:hre.ethers.provider, 
            tryAggregate: true, 
            multicallCustomContractAddress: _multiCall3.address  //the address of the deployed multicall
});

The provider should be OK. I tried with hre.ethers.getDefaultProvider(), but the blockNumber I was getting in the response made me realise it was not correct. I also checked this against goerli and with hre.ethers.provider I´m getting the correct block number.

After the multicall.call, the state of my contract is not being changed and it should.
I thought it could be something wrong in my contract implementation but I tried to replicate this using goerli. I noticed that no transaction is being sent to the blockchain after the multicall.call is done and I got success:true.

Multicallv3
https://goerli.etherscan.io/address/0xc1862322861093b5b6944c305963ea2e9ffe5519
ERC20PermitToken
https://goerli.etherscan.io/address/0x871663104598d79d41d89461cc4f18f71b01db3b

MultiCall.call

await multicall.call(contractCallContext);

ContractCallContext:

[
   {
      "reference":"ERC20PermitToken",
      "contractAddress":"0x871663104598d79D41d89461CC4F18f71b01db3b",
      "abi":[
         {
            "inputs":[
               {
                  "internalType":"address",
                  "name":"owner",
                  "type":"address"
               },
               {
                  "internalType":"address",
                  "name":"spender",
                  "type":"address"
               },
               {
                  "internalType":"uint256",
                  "name":"value",
                  "type":"uint256"
               },
               {
                  "internalType":"uint256",
                  "name":"deadline",
                  "type":"uint256"
               },
               {
                  "internalType":"uint8",
                  "name":"v",
                  "type":"uint8"
               },
               {
                  "internalType":"bytes32",
                  "name":"r",
                  "type":"bytes32"
               },
               {
                  "internalType":"bytes32",
                  "name":"s",
                  "type":"bytes32"
               }
            ],
            "name":"permit",
            "outputs":[
               
            ],
            "stateMutability":"nonpayable",
            "type":"function"
         }
      ],
      "calls":[
         {
            "reference":"permit",
            "methodName":"permit",
            "methodParameters":[
               "0x0C7ebAC9957C01F0b891B912895A811338aF4Ae0",
               "0x02af0F2eE01523357B11cBb1B86BA66eb8D2bfED",
               {
                  "type":"BigNumber",
                  "hex":"0x01"
               },
               {
                  "type":"BigNumber",
                  "hex":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
               },
               28,
               "0xef92a36fc7c2fa26ee4275d73918f03ae651708377026c4e936c76f39f9327ca",
               "0x0b2d9ef10de8200043ffccd2be22e68ef94efe96a1411dbff3aa57b913957da0"
            ]
         }
      ]
   }
]

Result: goerli execution

{
   "results":{
      "ERC20PermitToken":{
         "originalContractCallContext":{
            "reference":"ERC20PermitToken",
            "contractAddress":"0x871663104598d79D41d89461CC4F18f71b01db3b",
            "abi":[
               {
                  "inputs":[
                     {
                        "internalType":"address",
                        "name":"owner",
                        "type":"address"
                     },
                     {
                        "internalType":"address",
                        "name":"spender",
                        "type":"address"
                     },
                     {
                        "internalType":"uint256",
                        "name":"value",
                        "type":"uint256"
                     },
                     {
                        "internalType":"uint256",
                        "name":"deadline",
                        "type":"uint256"
                     },
                     {
                        "internalType":"uint8",
                        "name":"v",
                        "type":"uint8"
                     },
                     {
                        "internalType":"bytes32",
                        "name":"r",
                        "type":"bytes32"
                     },
                     {
                        "internalType":"bytes32",
                        "name":"s",
                        "type":"bytes32"
                     }
                  ],
                  "name":"permit",
                  "outputs":[
                     
                  ],
                  "stateMutability":"nonpayable",
                  "type":"function"
               }
            ],
            "calls":[
               {
                  "reference":"permit",
                  "methodName":"permit",
                  "methodParameters":[
                     "0x0C7ebAC9957C01F0b891B912895A811338aF4Ae0",
                     "0x02af0F2eE01523357B11cBb1B86BA66eb8D2bfED",
                     {
                        "type":"BigNumber",
                        "hex":"0x01"
                     },
                     {
                        "type":"BigNumber",
                        "hex":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                     },
                     28,
                     "0xef92a36fc7c2fa26ee4275d73918f03ae651708377026c4e936c76f39f9327ca",
                     "0x0b2d9ef10de8200043ffccd2be22e68ef94efe96a1411dbff3aa57b913957da0"
                  ]
               }
            ]
         },
         "callsReturnContext":[
            {
               "returnValues":"0x",
               "decoded":false,
               "reference":"permit",
               "methodName":"permit",
               "methodParameters":[
                  "0x0C7ebAC9957C01F0b891B912895A811338aF4Ae0",
                  "0x02af0F2eE01523357B11cBb1B86BA66eb8D2bfED",
                  {
                     "type":"BigNumber",
                     "hex":"0x01"
                  },
                  {
                     "type":"BigNumber",
                     "hex":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
                  },
                  28,
                  "0xef92a36fc7c2fa26ee4275d73918f03ae651708377026c4e936c76f39f9327ca",
                  "0x0b2d9ef10de8200043ffccd2be22e68ef94efe96a1411dbff3aa57b913957da0"
               ],
               "success":true
            }
         ]
      }
   },
   "blockNumber":8374483
}

The execution of: hre.ethers.provider.getBlock(results.blockNumber)

{
  hash: '0xb6ea7d514f8b7964a1e0a1a8c37b10cae160c32ad6e8a16689d4acda766a2ace',
  parentHash: '0x642723ef33389137adf06538431ec150f7bec04bb82e23723a78190532457477',
  number: 8374483,
  timestamp: 1674659052,
  nonce: '0x0000000000000000',
  difficulty: 0,
  gasLimit: BigNumber { value: "30000000" },
  gasUsed: BigNumber { value: "3196874" },
  miner: '0xf36F155486299eCAff2D4F5160ed5114C1f66000',
  extraData: '0x',
  transactions: [
    '0xf21146d6ee3ba5588f9d9b1ea40064dfd64897a6b496de0eeb49d9916d3add4f',
    '0xa14b09654b83657d54aaa2501ded1fdb869e08889cccf415f22daf26a55de6e5',
    '0x86e64c989291efd4fde735d1ef416abb195808696744357d13289c2fe789a41b',
    '0xc98927934e5e40787f9d715288ff246d04f1279415bc188880e39a9433f5b0f0',
    '0x96d34d26b0638e15d7cf124db4e752ac3f8a17340558aadcf3f8a73adcfd26d6',
    '0x27b2b5a598a27d3a5777b79fedb8181912fc72ad247deffacd603acbeeb965a7',
    '0xfb8a868734effabec77cadc3a2ae5f843312863493b5f72491fc13a420fb2ff9',
    '0x722452c2fa698cf1296eb43a7f5ee345985ba7be88f14f22cf085a0f2089a610',
    '0x2c4d81baeb0a042ad408b5efcd990add45442a9eb6ba49b34efb99275e5f989e',
    '0x023edeecba8bb5671f27920aea063eb54935c2bfa028b2be716866d43631c952'
  ],
  baseFeePerGas: BigNumber { value: "132" },
  _difficulty: BigNumber { value: "0" }
}

Using Multicall with ethers Wallet

Hi,

The comment in the code snippet says that

...ethers hold providers in other context like wallet, signer etc all can be passed in as well.

So, I assumed that the following code is correct

const provider = new ethers.providers.JsonRpcProvider(process.env.NETWORK_URL);
const wallet = new ethers.Wallet(process.env.ACCOUNT).connect(provider);
const multicall = new ethereumMulticall.Multicall({ ethersProvider: wallet , tryAggregate: true });

But I'm, getting

UnhandledPromiseRejectionWarning: TypeError: ethersProvider.getNetwork is not a function

What am I missing? How can use Multicall with Wallet?

Thank you

feature request: upgrade to Multicall3

Looks like you currently maintain a hardcoded mapping of networks to addresses, and need to deploy a new contract and update that for each chain users want supported. If you migrate to Multicall3 you'll support more chains with less maintenance (I'm biased of course, but I do think it'll be easier for you).

It's fully backwards compatible, and adds a few new methods to offer users more granularity over reverting calls.

Looks like etherlite is the only chain Multicall3 isn't deployed on that this lib currently supports: if people actually use that and you need to maintain compatibility, we just need someone to fund to the Multicall3 deployer account (0x05f32B3cC3888453ff71B01135B34FF8e41263F2) and I can deploy it on that chain.

Error when executing huge batches

Executing batches of huge sizes 300++ seems to throw an error. Suspect it has to do with timeout variable? Smaller batches with the same logic does not throw any errors.

Tron Mainnet

Can this library be used for the Tron mainnet?

Archive data request

If i get request for multicall data on specific block wich happened in 2023 i getting result as i should, but if i request data for Eth from 2019 im getting this error:

Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="tryBlockAndAggregate(bool,(address,bytes)[])", data="0x", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
at Logger.makeError (index.ts:269:1)
at Logger.throwError (index.ts:281:1)
at Interface.decodeFunctionResult (interface.ts:427:1)
at Object. (index.ts:400:1)
at Generator.next ()
at fulfilled (index.ts:1:1)

Is this because there is no data for that time so i can return emty result or i making some mistake.? I'm using this multicontractAddress : 0xcA11bde05977b3631167028862bE2a173976CA11
Also im using Merkle as provider

Custom block number

Love the library, thank you for sharing!

Is there any way to request information from a specific block number?
this example code gets your token balance from 4 days ago compared to now

    const blocksAgo = n => provider.getBlockNumber().then(b => b - n);
    let blockNumber = await blocksAgo(30000);

    const tokenContract = new Contract(tokenAddress, erc20Abi);
    let balance1 = await tokenContract.balanceOf(address, 'latest');
    console.log(`latestBalance: ${ethers.utils.formatEther(balance1)}`);
    let balance2 = await tokenContract.balanceOf(address, blockNumber);
    console.log(`prevBalance: ${ethers.utils.formatEther(balance2)}`);

function symbol returns empty

I'm trying to understand how this module works.
This is my ContractCallContext:

  {
        reference: 'testContract',
        contractAddress: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195',
        abi: [        {
            name: "symbol",
            type: "function",
            inputs: [{name: "", type: 'string'}],
            outputs: [
                {
                    name: "",
                    type: "string"
                }
            ],
            stateMutability: "view",

        },],
        calls: [{ reference: 'symbolCall', methodName: 'symbol', methodParameters: [42] }]
    }

returns this:

callsReturnContext: [
    {
      returnValues: [],
      decoded: false,
      reference: 'symbolCall',
      methodName: 'symbol',
      methodParameters: [Array],
      success: false
    }
  ]

What am I doing wrong?
Could you also show me an example of something more complex like erc20 token approval and swap?

thanks for this module.

Ethereum wallet for donate

Public pls your ETH wallet. Good way for donate.
You doing perfectly work, and save time of developers:)

swapExactETHForTokens transaction

How can I convert the following transaction to work in a multicall?

var contract = new web3.eth.Contract(routerAbi, routerAddress);
var swapETHforTokens = contract.methods.swapExactETHForTokens(0, [WBNB, tokenAddress], WalletAddress, timeStamp);
var encodedABI = swapETHforTokens.encodeABI();
var swapETHforTokensTX = {
   from: WalletAddress,
   to: routerAddress,
   gas: 176024,
   data: encodedABI,
   value: web3.utils.toWei(ETHtoSell, 'ether'),
};

No problem calling it with eth.call:

await web3.eth
   .call(swapETHforTokenstx, 'pending')
   .then((result) => {
      console.log(web3.eth.abi.decodeLog([{ 'internalType': 'uint256[]', 'name': 'amounts', 'type': 'uint256[]' }], result).amounts[1]);
   })
   .catch((revertReason) => console.log(revertReason));

Fantom

It seems like Fantom blockchain is not supported.

Im getting

Returned error: the method net_version does not exist/is not available

Multicall get balances of a native currency in a wallet

Hi, I would like to ask how can I get balance of a native token in a wallet. Like the below code,

import {ContractCallContext, Multicall,} from 'ethereum-multicall';
import {ethers} from 'ethers';
import ERC20_ABI from './abi.json' assert {type: 'json'}

let provider = ethers.getDefaultProvider("https://polygon-rpc.com/");

try {
    const walletAddress = "0xe7804c37c13166fF0b37F5aE0BB07A3aEbb6e245"
    const tokenAddresses = ["0x0000000000000000000000000000000000000000"]
    const network = await provider.getNetwork()
    // you can use any ethers provider context here this example is
    // just shows passing in a default provider, ethers hold providers in
    // other context like wallet, signer etc all can be passed in as well.
    const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true });

    const contractCallContext: ContractCallContext[] = tokenAddresses.map((it) => ({
        reference: `${it}`,
        contractAddress: it,
        abi: ERC20_ABI,
        calls: [{ reference: `callBalanceOf:${it}`, methodName: 'balanceOf', methodParameters: [walletAddress] }],
    }))
    const callResults = await multicall.call(contractCallContext)
    const tokenBalancesMap = {} as Record<string, bigint>
    for (const address in callResults.results) {
        try {
            tokenBalancesMap[address] = BigInt(callResults.results[address].callsReturnContext[0].returnValues[0].hex)
        } catch (e) {
            console.warn(`parsing token balance from multicall failed for token ${address} in chain ${network.chainId}`)
        }
    }
    console.log(tokenBalancesMap)
}catch (e) {
    console.error('!-- ERROR:', e)
    console.log('Is there anything to inspect?')
} finally {
}
  • input: token address as null address
  • output: empty object. For more info, if I set tryAggregate to false, the error would be
ERROR: Error: data out-of-bounds (length=0, offset=32, code=BUFFER_OVERRUN, version=abi/5.7.0)

In the particular case is polygon chain, I can replace null address with matic token address 0x0000000000000000000000000000000000001010, and it works well. But if moving to another chain like Ethereum and BNB, there are no address like that but the null address.
How could I call get balance of a native currency for a wallet in such chains? On the other hand, in the library https://github.com/indexed-finance/multicall?tab=readme-ov-file#querying-token-balances, I could able to get balance with null address.

Thank you and have a great day.
P.S: Really appreciate your multicall package, and wishes you have awesome features in the future.

Cleanup non issue

When my account was compromised a spam issue was created in this repo. I sincerely apologize. Cleaning up such issues via script.

Block-Specific Multicall

Is it possible to pass a block parameter to the multicall in order to execute queries at that specific block height?

In Ethers, a single query can be executed with a blocktag ({ blockTag: blockNumber }) in order to accomplish this.

Having similar functionality on multicalls would be incredible.

Does the repository support ethers ^6.6.0

I am using

const { Multicall } = require("ethereum-multicall");
const poolABI = require('./utils/uniswapPairV2ABI.json');
const { ethers } = require("ethers");
const { wsURL } = require("./config.js");
const { Web3 } = require("web3");

const rpcURL = "My RPC URL";
// const web3 = new Web3(rpcURL);
const calls = [
    { methodName: 'token0', methodParameters: [] },
    { methodName: 'token1', methodParameters: [] },
    { methodName: 'factory', methodParameters: [] }
];

// A pair address
const contractAddress = "0xB6909B960DbbE7392D405429eB2b3649752b4838";

const callObject = {
    reference: "result0",
    contractAddress: contractAddress,
    abi: poolABI,
    calls: calls
};

const provider = new ethers.JsonRpcProvider(rpcURL);
console.log(provider);

const myMulticall = new Multicall({
    ethersProvider: provider,
    tryAggregate: true,
});
async function getResult() {
    let resp = await myMulticall.call(callObject);
    console.log(resp.results.result0.callsReturnContext);

}
getResult();

It throws out the error
Error: invalid signer or provider (argument="signerOrProvider", value={}, code=INVALID_ARGUMENT, version=contracts/5.7.0)

The ethers version is 6.6.1. I want to ask if the error is caused by the version conflict.

Accessing Multicall3 Contract Methods

I am trying to access the build in multicall3 method getCurrentBlockTimestamp() using the following ABI

[
      {      
        inputs: [],
        name: "getCurrentBlockTimestamp",
        outputs: [
          {
            internalType: "uint256",
            name: "",
            type: "uint256"
          }
        ],
        stateMutability: "view",
        type: "function"
      }
  ]

with the following call in my contractCallContext

             { reference: 'blockTime', methodName: 'getCurrentBlockTimestamp', methodParameters: null }

Unfortunately I get the error:

rror: call revert exception; VM Exception while processing transaction: reverted with reason string "Multicall3: call failed" [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="aggregate((address,bytes)[])", data="0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000174d756c746963616c6c333a2063616c6c206661696c6564000000000000000000", errorArgs=["Multicall3: call failed"], errorName="Error", errorSignature="Error(string)", reason="Multicall3: call failed", code=CALL_EXCEPTION, version=abi/5.7.0)

How can I access the build in multicall contract methods? I cannot find anything in the readme

Execution failed

const ethereumMulticall = require("ethereum-multicall");
const { ethers } = require("ethers");

//   let provider = ethers.getDefaultProvider();

async function go() {
  // you can use any ethers provider context here this example is
  // just shows passing in a default provider, ethers hold providers in
  // other context like wallet, signer etc all can be passed in as well.
  const multicall = new ethereumMulticall.Multicall({
    nodeUrl: "https://bsc-pokt.nodies.app",
    tryAggregate: true,
  });

  const contractCallContext = [
    {
      reference: "testContract2",
      contractAddress: "0x55d398326f99059fF775485246999027B3197955",
      abi: [
        {
          name: "totalSupply",
          type: "function",
          inputs: [],
          outputs: [{ name: "", type: "uint256" }],
        },
      ],
      calls: [
        {
          reference: "fooTwoCall",
          methodName: "totalSupply",
          methodParameters: [],
        },
      ],
    },
  ];

  const results = await multicall.call(contractCallContext);
  console.log(results);
}

go();

Fail msg

/Users/js/node_modules/@ethersproject/logger/lib/index.js:238
        var error = new Error(message);
                    ^

Error: unable to determine stateMutability (argument="value", value={"name":"totalSupply","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256"}]}, code=INVALID_ARGUMENT, version=abi/5.7.0)
    at Logger.makeError (/Users/js/node_modules/@ethersproject/logger/lib/index.js:238:21)
    at Logger.throwError (/Users/ll/code/js/node_modules/@ethersproject/logger/lib/index.js:247:20)
    at Logger.throwArgumentError (/Users/ll/code/js/node_modules/@ethersproject/logger/lib/index.js:250:21)
    at verifyState (/Users/ll/code/js/node_modules/@ethersproject/abi/lib/fragments.js:588:16)
    at FunctionFragment.fromObject (/Users/ll/code/js/node_modules/@ethersproject/abi/lib/fragments.js:728:21)
    at Fragment.fromObject (/Users/ll/code/js/node_modules/@ethersproject/abi/lib/fragments.js:371:41)
    at Fragment.from (/Users/ll/code/js/node_modules/@ethersproject/abi/lib/fragments.js:363:25)
    at /Users/ll/code/js/node_modules/@ethersproject/abi/lib/interface.js:101:41
    at Array.map (<anonymous>)
    at new Interface (/Users/ll/code/js/node_modules/@ethersproject/abi/lib/interface.js:100:65) {
  reason: 'unable to determine stateMutability',
  code: 'INVALID_ARGUMENT',
  argument: 'value',
  value: {
    name: 'totalSupply',
    type: 'function',
    inputs: [],
    outputs: [ { name: '', type: 'uint256' } ]
  }
}

Node.js v18.17.1

"ethereum-multicall": "^2.21.0",
"ethers": "^5.7.0",

What's the matter with the code?

returnValues is an array of values instead of an array of objects

Hey everyone.
I've encountered an issue where after the multicall, the returnValues prop in the callsReturnContext item is an array (of bignums in my case), instead of an array of objects with key/value pairs.
It's the first time I've encountered such an issue because I've always used this lib for multicalls but it turned out that all of those always had a singular return, meaning that they returned one value and not a collection.
The ABI is correct, I triple checked. Everything works as expected if I call the contract directly.
I'm providing screenshots for context, however, if those are not enough - please let me know, I'll create a codepen demo.

Screenshot 2023-04-25 at 23 16 26

Screenshot 2023-04-25 at 23 17 17

Screenshot 2023-04-25 at 23 17 57

Screenshot 2023-04-25 at 23 18 43

Multicall2 (tryAggregate) missing from Matic

It seems that the variant that allows for failing calls wasn't yet deployed to Matic/Mumbai. The official maker repo doesn't include addresses for Multicall2 and the file with configurations in the library unfortunately mixes the usage of v1 and v2.

As a heads up for anyone using the aggregate variant, don't forget to manually reference a multicall2 contract in the constructor. If you want to use something already deployed, I've added them on Matic and Mumbai if anyone wants to use them there:

Matic: 0x275617327c958bd06b5d6b871e7f491d76113dd8
Mumbai: 0xe9939e7Ea7D7fb619Ac57f648Da7B1D425832631

P.S. Great work with the library, was really useful!

ethers.js Signer TypeError: ethersProvider.getNetwork

Use ethers.js

new Multicall({ ethersProvider: new providers.Web3Provider((window as any).ethereum), tryAggregate: true })

Get error:
TypeError: ethersProvider.getNetwork is not a function

PS: Metamask wallet connected

Multicall do not run in BSC TESTNET

  • I use muticall to call in bsc testnet with web3js but it cause a error Error: Returned error: execution reverted.
  • I am sure my contract address and ABI is correct because I am able to call this contract without muticall.

Could anyone tell me if I missed anything?

Full unit tests

Did not have time to cover this with unit tests so when I get time make sure we cover the lot before it's used in many projects.

'Cannot read properties of undefined' while call

I have parts of code:

this.multicall = new ethereumMulticall .Multicall({ nodeUrl: config.rpc, tryAggregate: true });
or
this.multicall = new ethereumMulticall .Multicall({ ethersProvider: connect.provider, tryAggregate: true });

and

let answ = await this.multicall.call({});
or
answ = await this.multicall.call(multicallContext);

All combinations (with empty context too) executing gives same error:
`
/node_modules/ethereum-multicall/node_modules/@ethersproject/abi/lib/interface.js:101
properties_1.defineReadOnly(this, "fragments", abi.map(function (fragment) {
^

TypeError: Cannot read properties of undefined (reading 'map')

`

How to get eth call data

I need the hex data to use my custom eth_call like:
image

So instead of using multicall.call(), I just want to get hex data.

Have any methods there to do this?

invalid address

Hi, I got this error:

Uncaught (in promise) Error: invalid address (argument="address", value=42, code=INVALID_ARGUMENT, version=address/5.4.0) (argument="_owner", value=42, code=INVALID_ARGUMENT, version=abi/5.4.0)
at Logger.makeError (swapv1.1.1.f4473701.js:66157)
at Logger.throwError (swapv1.1.1.f4473701.js:66166)
at Logger.throwArgumentError (swapv1.1.1.f4473701.js:66169)
at AddressCoder._throwError (swapv1.1.1.f4473701.js:149229)
at AddressCoder.encode (swapv1.1.1.f4473701.js:149357)
at swapv1.1.1.f4473701.js:149419
at Array.forEach ()
at Object.pack (swapv1.1.1.f4473701.js:149407)
at TupleCoder.encode (swapv1.1.1.f4473701.js:149782)
at AbiCoder.encode (swapv1.1.1.f4473701.js:149169)

`const multicall = new Multicall({ ethersProvider: provider, tryAggregate: true });

const contractCallContext: ContractCallContext<{extraContext: string, foo4: boolean}>[] = [
    {
        reference: 'testContract',
        contractAddress: '0x8df3aad3a84da6b69a4da8aec3ea40d9091b2ac4',
        abi: Abi,
        calls: [{ reference: 'fooCall', methodName: 'balanceOf', methodParameters: [42] }],

    },
    {
        reference: 'testContract2',
        contractAddress: '0x1a13f4ca1d028320a707d99520abfefca3998b7f',
        abi: Abi,
        calls: [{ reference: 'fooTwoCall', methodName: 'balanceOf', methodParameters: [42] }]
    }
];

const results: ContractCallResults = await multicall.call(contractCallContext);
console.log(results);

}`

can you help me?

Multicall fails on Arbitrum for most RPCs

When I try to perform a multicall on Arbitrum, most RPCs will return this error:
console.error("getUniswapV3PairPrice ERROR", networkId, e)
The only RPC I've tried that does work on Arbitrum is: https://arb1.arbitrum.io/rpc
On other networks (I tried mainnet, Optimism and Goerli) the multicalls work perfectly.

I create my multicall instances like this:
const multicall = new Multicall({ web3Instance: web3, tryAggregate: true });
With web3 here being new Web3(myRpcUrl). I have also tried using nodeUrl: myRpcUrl instead of web3Instance, to no avail.

use custom contract address

I want to use the contract that I deploy on okexchain, but there is something wrong:
image
if I open the 'tryAggregate'
image
if turn off 'tryAggregate', I can get a result,bug the balance of token is wrong
image
image

can u tell me how can use a custom contract?

Uncaught (in promise) Error: network does not support ENS (operation="ENS", network="unknown", code=UNSUPPORTED_OPERATION, version=providers/5.1.2)

Hello try to using it on browser via vue.js but got error :
index.js?ffb2:166 Uncaught (in promise) Error: network does not support ENS (operation="ENS", network="unknown", code=UNSUPPORTED_OPERATION, version=providers/5.1.2)
at Logger.makeError (index.js?ffb2:166)
at Logger.throwError (index.js?ffb2:175)
at JsonRpcProvider.eval (base-provider.js?bc79:1244)
at Generator.next ()
at fulfilled (base-provider.js?bc79:5)

Is it because doesnt support on browser ?

here is my initialization :

const ethersProvider = new providers.getDefaultProvider('https://bsc-dataseed1.binance.org');
    const multicall = new Multicall({ ethersProvider: ethersProvider, multicallCustomContractAddress: '0x1Ee38d535d541c55C9dae27B12edf090C608E6Fb' });

UnhandledPromiseRejectionWarning: Error: Returned error: out of gas

This code returns "UnhandledPromiseRejectionWarning: Error: Returned error: out of gas".

Any idea why?

const { Multicall } = require('ethereum-multicall');
const Web3 = require('web3');
const { abi } = require('../metadata/abi');

const options = {
  timeout: 10000, // ms
  reconnect: { auto: true, delay: 1000,  maxAttempts: 5, onTimeout: false }
};
const provider = new Web3.providers.HttpProvider('ADDRESS HERE');
const web3 = new Web3(provider, options);
const multicall = new Multicall({ web3Instance: web3, tryAggregate: true});

async function main() {
  const tokenIDs = Array.from({length: 296}, (_, i) => i + 1900);
  const calls = tokenIDs.map((token_id) => { return { methodName: 'tokenByIndex', methodParameters: [token_id] }; });
  const contractCallContext = [{ reference: 'contract', contractAddress: '0x160c404b2b49cbc3240055ceaee026df1e8497a0', abi, calls}];
  const results = await multicall.call(contractCallContext);
  const extract = results.results.contract.callsReturnContext.map((r) => r.success ? web3.utils.hexToNumber(r.returnValues[0].hex) : null);
  console.log(extract);
}

main();

Passing an array in CallContext entry

Hi I am receiving this error when trying to call getAmountsOut on uniswap router

Error: call revert exception; VM Exception while processing transaction: reverted with reason string "Multicall aggregate: call failed" [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="aggregate((address,bytes)[])", data="0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000204d756c746963616c6c206167677265676174653a2063616c6c206661696c6564", errorArgs=["Multicall aggregate: call failed"], errorName="Error", errorSignature="Error(string)", reason="Multicall aggregate: call failed", code=CALL_EXCEPTION, version=abi/5.6.3) at Logger.makeError (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/logger/src.ts/index.ts:261:28) at Logger.throwError (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/logger/src.ts/index.ts:273:20) at Interface.decodeFunctionResult (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/abi/src.ts/interface.ts:427:23) at Object.<anonymous> (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/contracts/src.ts/index.ts:400:44) at step (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/contracts/lib/index.js:48:23) at Object.next (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/contracts/lib/index.js:29:53) at fulfilled (/Users/johnathonlam/Documents/work/data-collection/node_modules/@ethersproject/contracts/lib/index.js:20:58) at processTicksAndRejections (node:internal/process/task_queues:96:5) { reason: 'Multicall aggregate: call failed', code: 'CALL_EXCEPTION', method: 'aggregate((address,bytes)[])', data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000204d756c746963616c6c206167677265676174653a2063616c6c206661696c6564', errorArgs: [ 'Multicall aggregate: call failed' ], errorName: 'Error', errorSignature: 'Error(string)', address: '0xC50F4c1E81c873B2204D7eFf7069Ffec6Fbe136D', args: [ [ [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object], [Object] ] ],

The way I am trying to pass in the data is like

{ reference: info.address + '_' + info.token1, contractAddress: chainConfig.bsc.dex.pancakeswap.router, abi: uniswapRouterAbi, calls: [ { reference: 'amount', methodName: 'getAmountsOut', methodParameters: [info.token1reserve, [info.token1, chainConfig.bsc.dex.pancakeswap.nativeCurrency]] }, ], }

tryAggregate fails when there's an error decoding.

I'm trying to get a list of tokens for a user's wallet, and there's a contract that is likely malformed. The blockchain call works, but when multicall tries to decode with the ABI (symbol, balanceOf, name, decimals, totalSupply) it fails w/ a data out of bounds error:
Error: data out-of-bounds (length=0, offset=32, code=BUFFER_OVERRUN, version=abi/5.5.0)

If we just wrapped the ABI decode portion of multicall.js with a try/catch, we could still get the expected behavior of aggregation despite what happens during calls to the network and decoding.

Here's the token (it's a Uniswap v2 LP token):
https://etherscan.io/token/0xfb072e4dE3719E84503b3fB303173Dd6D29aB472#readContract

When I call the same methods manually they work

Map reference as contract address

Hello. Is it possible to map a reference from 1 contract call to be passed as a value to another contract call?

So I would like to get a PAIR of a token-BnB and then use that pair address to call getReserves

[
    {
      reference: 'factory',
      contractAddress: factoryAddress,
      abi: factoryABI,
      calls: [
        { reference: 'tokenPair', methodName: 'getPair', methodParameters: [tokenAddress, bnbAddress] },
      ]
    },
    {
      reference: 'liquidity',
      contractAddress: tokenPair,   <-----------------------------------------------------------
      abi: [
        {
          'constant': true,
          'inputs': [],
          'name': 'getReserves',
          'outputs': [
            { 'internalType': 'uint112', 'name': '_reserve0', 'type': 'uint112' },
            { 'internalType': 'uint112', 'name': '_reserve1', 'type': 'uint112' },
            { 'internalType': 'uint32', 'name': '_blockTimestampLast', 'type': 'uint32' }
          ],
          'payable': false,
          'stateMutability': 'view',
          'type': 'function'
        }
      ],
      calls: [
        { methodName: 'getReserves' },
      ]
    }
  ]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.