1

In some cases, there is the need to gather synchronized data from the blockchain. A good example is querying token prices from Uniswap. One does not want to have the price for one token from block x and a price for another token at block y.

Multicall aggregates all queries to different contracts in one call, so it guarantees your data is retrieved from the same block.

But how does one do a multicall on ShimmerEVM?

1 Answer 1

1

This is a minimal reproduction of a MultiCall call that simulates both view call functionality and simulated transaction functionality.

const { ethers } = require('ethers');

// Multicallv2 address, current one exists on the ShimmerEVM Testnet
const multicallAddress = '0x32f20190540CCc55E137928c11d70d24daf52995';

// Simple counter contract deployed on ShimmerEVM Testnet
const testCounterAddress = '0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0';

const multicallABI = [ 
    {   
        inputs: [
            {
                components: [
                    { internalType: 'address', name: 'target', type: 'address' },
                    { internalType: 'uint256', name: 'gasLimit', type: 'uint256' },
                    { internalType: 'bytes', name: 'callData', type: 'bytes' }
                ],
                internalType: 'struct TangleswapInterfaceMulticall.Call[]',
                name: 'calls',
                type: 'tuple[]'
            }
        ],
        name: 'multicall',
        outputs: [
            { internalType: 'uint256', name: 'blockNumber', type: 'uint256' },
            {
                components: [
                    { internalType: 'bool', name: 'success', type: 'bool' },
                    { internalType: 'uint256', name: 'gasUsed', type: 'uint256' },
                    { internalType: 'bytes', name: 'returnData', type: 'bytes' }
                ],
                internalType: 'struct TangleswapInterfaceMulticall.Result[]',
                name: 'returnData',
                type: 'tuple[]'
            }
        ],
        stateMutability: 'nonpayable',
        type: 'function'
    }   
];

// Create a provider instance for the JSON-RPC endpoint
const provider = new ethers.providers.JsonRpcProvider('https://json-rpc.evm.testnet.shimmer.network/');

// Create an instance of the multicall contract
const multicallContract = new ethers.Contract(multicallAddress, multicallABI, provider);


// Simple function to generate the function signature needed for calling multicall
function getFunctionSignature(name) {
  return ethers.utils.keccak256(
    ethers.utils.toUtf8Bytes(name)
  ).slice(0, 10);
}

const fsig = getFunctionSignature('incrementCounter()');
const gsig = getFunctionSignature('getCount()');

// Define the calls to be made
let calls = Array(6).fill().map(() => ({
    target: testCounterAddress,
    gasLimit: 500000,
    callData: fsig // function signature of 'incrementCounter()'
}));

// Add a getCount at the start of the multicall
calls.unshift({
    target: testCounterAddress,
    gasLimit: 500000,
    callData: gsig // function signature of 'getCount()'
});

// Add a getCount at the end of the multicall
calls.push({
    target: testCounterAddress,
    gasLimit: 500000,
    callData: gsig // function signature of 'getCount()'
});

console.log('Calls sent to MultiCall:', calls);

// Convenience function to format the return data of MultiCall
async function makeMulticall(multicallContract, calls, callback) {
  const result = await multicallContract.callStatic.multicall(calls);
  const returnData = result.returnData;

  const parsedData = returnData.map((result, i) => {
    const call = calls[i];
    const target = call.target;
    const callData = call.callData;


    return {
      target,
      callData,
      gasUsed: result.gasUsed.toString(),
      success: result.success,
      result: result.returnData
    };
  });

  callback(parsedData);
}

// Calling the multicall convenience function wtih a simple debug callback
makeMulticall(multicallContract, calls, function(data) { console.log(data) });

Expected output:

[
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0xa87d942c",
    gasUsed: "985",
    success: true,
    result: "0x0000000000000000000000000000000000000000000000000000000000000000"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "20979",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "3880",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "3880",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "3880",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "3880",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0x5b34b966",
    gasUsed: "3880",
    success: true,
    result: "0x"
  },
  {
    target: "0x3Ba7B9Ed5dbF0509c6DfbD8d4015D826AdB305d0",
    callData: "0xa87d942c",
    gasUsed: "985",
    success: true,
    result: "0x0000000000000000000000000000000000000000000000000000000000000006"
  }
]

Credit goes to Dave see this GitHub repo for the full examples.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.