Skip to content

Instantly share code, notes, and snippets.

@miguelmota
Created June 12, 2021 01:10
Show Gist options
  • Save miguelmota/e2f90037eb4c4bd3f3d3711778ca5635 to your computer and use it in GitHub Desktop.
Save miguelmota/e2f90037eb4c4bd3f3d3711778ca5635 to your computer and use it in GitHub Desktop.
Golang MultiCall solidity contract call example
package main
import (
"context"
"encoding/hex"
"fmt"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
var bytecode = "0x608060405234801561001057600080fd5b5060405161074f38038061074f83398181016040528101906100329190610362565b6000825190508082511461007b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100729061054d565b60405180910390fd5b60608167ffffffffffffffff8111801561009457600080fd5b506040519080825280602002602001820160405280156100c857816020015b60608152602001906001900390816100b35790505b50905060008090505b828110156101d45760008582815181106100e757fe5b6020026020010151905060608583815181106100ff57fe5b60200260200101519050600060608373ffffffffffffffffffffffffffffffffffffffff16836040516101329190610536565b6000604051808303816000865af19150503d806000811461016f576040519150601f19603f3d011682016040523d82523d6000602084013e610174565b606091505b5091509150816101aa576040518060200160405280600081525086868151811061019a57fe5b60200260200101819052506101c3565b808686815181106101b757fe5b60200260200101819052505b5050505080806001019150506100d1565b50606043826040516020016101ea92919061056d565b60405160208183030381529060405290508060208201f35b60008151905061021181610737565b92915050565b600082601f83011261022857600080fd5b815161023b610236826105ca565b61059d565b9150818183526020840193506020810190508385602084028201111561026057600080fd5b60005b8381101561029057816102768882610202565b845260208401935060208301925050600181019050610263565b5050505092915050565b600082601f8301126102ab57600080fd5b81516102be6102b9826105f2565b61059d565b9150818183526020840193506020810190508360005b8381101561030457815186016102ea888261030e565b8452602084019350602083019250506001810190506102d4565b5050505092915050565b600082601f83011261031f57600080fd5b815161033261032d8261061a565b61059d565b9150808252602083016020830185838301111561034e57600080fd5b6103598382846106f3565b50505092915050565b6000806040838503121561037557600080fd5b600083015167ffffffffffffffff81111561038f57600080fd5b61039b85828601610217565b925050602083015167ffffffffffffffff8111156103b857600080fd5b6103c48582860161029a565b9150509250929050565b60006103da8383610457565b905092915050565b60006103ed82610656565b6103f78185610679565b93508360208202850161040985610646565b8060005b85811015610445578484038952815161042685826103ce565b94506104318361066c565b925060208a0199505060018101905061040d565b50829750879550505050505092915050565b600061046282610661565b61046c818561068a565b935061047c8185602086016106f3565b61048581610726565b840191505092915050565b600061049b82610661565b6104a5818561069b565b93506104b58185602086016106f3565b80840191505092915050565b60006104ce6022836106a6565b91507f4572726f723a204172726179206c656e6774687320646f206e6f74206d61746360008301527f682e0000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b610530816106e9565b82525050565b60006105428284610490565b915081905092915050565b60006020820190508181036000830152610566816104c1565b9050919050565b60006040820190506105826000830185610527565b818103602083015261059481846103e2565b90509392505050565b6000604051905081810181811067ffffffffffffffff821117156105c057600080fd5b8060405250919050565b600067ffffffffffffffff8211156105e157600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561060957600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561063157600080fd5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b60006106c2826106c9565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60005b838110156107115780820151818401526020810190506106f6565b83811115610720576000848401525b50505050565b6000601f19601f8301169050919050565b610740816106b7565b811461074b57600080fd5b5056fe"
const MulticallABI = "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"bytes[]\",\"name\":\"datas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
const ERC20ABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"spender\",\"type\":\"address\"},{\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"from\",\"type\":\"address\"},{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"tokenOwner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"tokenOwner\",\"type\":\"address\"},{\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"tokenOwner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]"
func main() {
client, err := ethclient.Dial("https://mainnet.infura.io")
if err != nil {
panic(err)
}
msg := ethereum.CallMsg{}
bytecodeBytes, err := hex.DecodeString(strings.ReplaceAll(bytecode, "0x", ""))
if err != nil {
panic(err)
}
abiErc20, err := abi.JSON(strings.NewReader(string(ERC20ABI)))
if err != nil {
panic(err)
}
abiMultiCall, err := abi.JSON(strings.NewReader(string(MulticallABI)))
if err != nil {
panic(err)
}
method := abiErc20.Methods["balanceOf"]
id := method.ID
inputs, err := method.Inputs.Pack(
common.HexToAddress("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"),
)
if err != nil {
panic(err)
}
inputs = append(id[:], inputs[:]...)
targets := []common.Address{
common.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f"),
}
datas := [][]byte{
inputs,
}
inputData, err := abiMultiCall.Constructor.Inputs.Pack(targets, datas)
if err != nil {
panic(err)
}
msg.Data = append(bytecodeBytes[:], inputData[:]...)
result, err := client.PendingCallContract(context.TODO(), msg)
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(result))
}
const ethers = require('ethers')
const { MultiCall } = require('@indexed-finance/multicall')
const abi = require('./ERC20.json')
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io')
const multi = new MultiCall(provider);
const inputs = []
inputs.push({ target: '0x6b175474e89094c44da98b954eedeac495271d0f', function: 'balanceOf', args: ['0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'] })
const result = await multi.multiCall(abi, inputs)
console.log(result)
@DiveInto
Copy link

DiveInto commented Dec 5, 2022

curious why the multicallABI defines only a constructor?

finally get why use constructor here, the multicall contract here used is not a deployed one(like this), but "deployed" when the query is made, and the actually query happens in the constructor, clever trick.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment