bguiz
bguiz

Reputation: 28587

How to aggregate multiple smart contract function calls on Rootstock?

I have multiple ERC20 tokens, deployed on Rootstock, and I want to track their balances and allowances in a real time from my DApp. In ethers.js, I can track their balances by alternately calling the functions balanceOf(address) and allowance(owner, spender). However, across two tokens, that equates to about 4 JSON-RPC requests every ~30 seconds.

I would prefer to reduce the frequency of JSON-RPC requests made by my application, by aggregating these particular requests.

Is it possible to combine multiple smart contract data queries into a single JSON-RPC request via ethers.js or any other library?

Upvotes: 6

Views: 3036

Answers (2)

Aleks Shenshin
Aleks Shenshin

Reputation: 2196

You could use the ethereum-multicall project, which consists of:

Smart contract deployments:

The advantage of this project is that both Rootstock Mainnet and Rootstock Testnet have the Multicall3 deployments:

... and bonus points for the 0xca11...ca11 vanity address ;)

To make aggregated smart contract calls:

  1. Install the npm package:
npm i ethereum-multicall
  1. Import the library to your project:
const { Multicall } = require('ethereum-multicall');
  1. Create Multicall instance and connect it to ethers.js provider
const multicall = new Multicall({
    ethersProvider: ethersProvider,
    tryAggregate: true,
});
  1. Create aggregated call:
  const aggregatedCall = [
    {
      reference: 'token1',
      contractAddress: token1Address,
      abi: token1ABI,
      calls: [
        {
          methodName: 'balanceOf',
          methodParameters: [walletAddress],
        },
        {
          methodName: 'allowance',
          methodParameters: [ownerAddress, spenderAddress],
        },
      ],
    },
    {
      reference: 'token2',
      contractAddress: token2Address,
      abi: token2ABI,
      calls: [
        {
          methodName: 'balanceOf',
          methodParameters: [walletAddress],
        },
        {
          methodName: 'allowance',
          methodParameters: [ownerAddress, spenderAddress],
        },
      ],
    },
  ];
  const { results } = await multicall.call(aggregatedCall);
  1. Extract the required data from the results object

Upvotes: 5

Aleks Shenshin
Aleks Shenshin

Reputation: 2196

You could take a look at @0xsequence/multicall. It consists of:

Smart contract deployments:

To make aggregated smart contract calls:

  1. Install the npm package:
npm i @0xsequence/multicall
  1. Import the library to your project:
const { providers } = require('@0xsequence/multicall');
  1. Create a Rootstock Testnet configuration:
const multicallConfig = {
  // RSK Testnet
  31: {
    // maximum number of calls to batch into a single JSON-RPC call
    batchSize: 50,
    // defines the time each call is held on buffer waiting for subsequent calls before aggregation, ms
    timeWindow: 50,
    // MultiCallUtils smart contract
    contract: '0xb39d1Dea1bF91Aef02484F677925904b9d6936B4',
  },
};
  1. Wrap your ethers.js current provider with a Multicall provider:
const multicallProvider = new providers.MulticallProvider(ethersProvider, multicallConfig[31]);
  1. Connect your token smart contracts to the Multicall provider instead of ethers.js provider to be to able make aggregated calls:
const token1 = new ethers.Contract(
    token1Address,
    token1ABI,
    multicallProvider,
  );
const token2 = ...
  1. Now you are ready to create an aggregated call:
function makeAggregatedCall() {
  const aggregatedCall = [
    multicallProvider.getBalance(address),
    token1.balanceOf(address),
    token1.allowance(owner, spender),
    token2.balanceOf(address),
    token2.allowance(owner, spender),
  ];
  [
    rbtcBalance,
    balance1,
    allowance1,
    balance2,
    allowance2,
  ] = await Promise.all(aggregatedCall);
}

The library will attempt to send all these function calls within a single call to MultiCallUtils smart contract multiCall() function. However if the aggregated call fails, as a fallback, its constituent functions calls will be called individually via the ethers.js provider.

  1. In order to subscribe to every newly minted block, attach the makeAggregatedCall function to the ethers provider block event listener:
ethersProvider.on('block', makeAggregatedCall);

Upvotes: 7

Related Questions