Search code examples
javascriptsolidityethers.jsrsk

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?


Solution

  • 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);