diff --git a/cmd/loadtest/account.go b/cmd/loadtest/account.go index d33088da..5da775de 100644 --- a/cmd/loadtest/account.go +++ b/cmd/loadtest/account.go @@ -117,8 +117,6 @@ type AccountPool struct { latestBlockNumber uint64 pendingTxsCache *uint64 - - multicall3Addr *common.Address } // Creates a new account pool with the given funding private key. @@ -165,24 +163,6 @@ func NewAccountPool(ctx context.Context, client *ethclient.Client, fundingPrivat return nil, fmt.Errorf("unable to get latestBlockNumber: %w", err) } - tops, err := bind.NewKeyedTransactorWithChainID(fundingPrivateKey, chainID) - if err != nil { - log.Error(). - Err(err). - Msg("unable create transaction signer") - return nil, fmt.Errorf("unable create transaction signer: %w", err) - } - - log.Debug().Msg("checking if multicall3 is supported") - multicall3Addr, _ := util.IsMulticall3Supported(ctx, client, true, tops, nil) - if multicall3Addr != nil { - log.Info(). - Stringer("address", multicall3Addr). - Msg("multicall3 is supported and will be used to fund accounts") - } else { - log.Info().Msg("multicall3 is not supported, will use EOA transfers to fund accounts") - } - ap := &AccountPool{ currentAccountIndex: 0, client: client, @@ -193,7 +173,6 @@ func NewAccountPool(ctx context.Context, client *ethclient.Client, fundingPrivat accountsPositions: make(map[common.Address]int), latestBlockNumber: latestBlockNumber, clientRateLimiter: rate.NewLimiter(rate.Every(50*time.Millisecond), 1), - multicall3Addr: multicall3Addr, } if !ap.isFundingEnabled() { @@ -463,13 +442,23 @@ func (ap *AccountPool) FundAccounts(ctx context.Context) error { return errors.New(errMsg) } - if ap.multicall3Addr != nil { - return ap.fundAccountsWithMulticall3(ctx, tops) + log.Debug().Msg("checking if multicall3 is supported") + multicall3Addr, _ := util.IsMulticall3Supported(ctx, ap.client, true, tops, nil) + if multicall3Addr != nil { + log.Info(). + Stringer("address", multicall3Addr). + Msg("multicall3 is supported and will be used to fund accounts") + } else { + log.Info().Msg("multicall3 is not supported, will use EOA transfers to fund accounts") + } + + if multicall3Addr != nil { + return ap.fundAccountsWithMulticall3(ctx, tops, multicall3Addr) } return ap.fundAccountsWithEOATransfers(ctx, tops) } -func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bind.TransactOpts) error { +func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bind.TransactOpts, multicall3Addr *common.Address) error { log.Debug(). Msg("funding sending accounts with multicall3") @@ -513,7 +502,7 @@ func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bin } mu.Lock() defer mu.Unlock() - tx, iErr := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, ap.multicall3Addr) + tx, iErr := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, multicall3Addr) if iErr != nil { log.Error().Err(iErr).Msg("failed to fund accounts with multicall3") return diff --git a/util/multicall3.go b/util/multicall3.go index 260c6d4e..0479fbd4 100644 --- a/util/multicall3.go +++ b/util/multicall3.go @@ -13,7 +13,11 @@ import ( "github.com/rs/zerolog/log" ) -const DefaultMulticall3Addr = "0xcA11bde05977b3631167028862bE2a173976CA11" +const ( + Multicall3SCAddr = "0xcA11bde05977b3631167028862bE2a173976CA11" + Multicall3DeployerAddress = "0x05f32b3cc3888453ff71b01135b34ff8e41263f2" + Multicall3SignedDeployTX = "0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086" +) // Contract CALL (Multicall3) must pay all CALL-path costs, including: // New account surcharge: 25,000 gas when value creates a previously empty account. @@ -27,7 +31,7 @@ const DefaultMulticall3Addr = "0xcA11bde05977b3631167028862bE2a173976CA11" const estimatedGasNeededToFundASingleAccount = 40000 // estimated gas per account to fund with multicall3 + margin(3k) const maxAccsToFundPerTx = 700 // arbitrary limit to avoid too large transactions -func Multicall3Deploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) { +func Multicall3IndeterministicDeploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) { address, tx, instance, err := multicall3.DeployMulticall3(sender, c) if err != nil { return common.Address{}, nil, nil, err @@ -35,10 +39,66 @@ func Multicall3Deploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Ad return address, tx, instance, nil } +func Multicall3DeterministicDeploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) { + deployerAddr := common.HexToAddress(Multicall3DeployerAddress) + + // check deployer balance + balance, err := c.BalanceAt(context.Background(), deployerAddr, nil) + if err != nil { + return common.Address{}, nil, nil, err + } + + gasLimit := uint64(1000000) + gasPrice := big.NewInt(100000000000) // 100 Gwei + maxDeployCost := big.NewInt(0).Mul(big.NewInt(int64(gasLimit)), gasPrice) // 0.1 ETH + if balance.Cmp(maxDeployCost) < 0 { + // fund deployer + nonce, iErr := c.PendingNonceAt(context.Background(), sender.From) + if iErr != nil { + return common.Address{}, nil, nil, iErr + } + suggestedGasprice, iErr := c.SuggestGasPrice(context.Background()) + if iErr != nil { + return common.Address{}, nil, nil, iErr + } + txFund := types.NewTransaction(nonce, deployerAddr, maxDeployCost, 21000, suggestedGasprice, nil) + signedTxFund, iErr := sender.Signer(sender.From, txFund) + if iErr != nil { + return common.Address{}, nil, nil, iErr + } + if iErr = c.SendTransaction(context.Background(), signedTxFund); iErr != nil { + return common.Address{}, nil, nil, iErr + } + receipt, iErr := bind.WaitMined(context.Background(), c, signedTxFund.Hash()) + if iErr != nil || receipt == nil || receipt.Status != 1 { + return common.Address{}, nil, nil, iErr + } + } + + rawTxBytes := common.FromHex(Multicall3SignedDeployTX) + tx := new(types.Transaction) + err = tx.UnmarshalBinary(rawTxBytes) + if err != nil { + return common.Address{}, nil, nil, err + } + + err = c.SendTransaction(context.Background(), tx) + if err != nil { + return common.Address{}, nil, nil, err + } + + address := common.HexToAddress(Multicall3SCAddr) + instance, err := multicall3.NewMulticall3(address, c) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, instance, nil +} + func Multicall3Exists(ctx context.Context, c *ethclient.Client, customAddr *common.Address) (common.Address, bool, error) { scAddr := customAddr if scAddr == nil { - addr := common.HexToAddress(DefaultMulticall3Addr) + addr := common.HexToAddress(Multicall3SCAddr) scAddr = &addr } @@ -67,7 +127,7 @@ func Multicall3Exists(ctx context.Context, c *ethclient.Client, customAddr *comm func Multicall3New(c *ethclient.Client, customAddr *common.Address) (common.Address, *multicall3.Multicall3, error) { scAddr := customAddr if scAddr == nil { - addr := common.HexToAddress(DefaultMulticall3Addr) + addr := common.HexToAddress(Multicall3SCAddr) scAddr = &addr } @@ -97,17 +157,28 @@ func IsMulticall3Supported(ctx context.Context, c *ethclient.Client, tryDeployIf return nil, false } - addr, tx, _, err := Multicall3Deploy(c, tops) - if err != nil { - return nil, false + // try to deploy it deterministically first + addr, tx, _, err := Multicall3DeterministicDeploy(c, tops) + if err == nil { + receipt, iErr := bind.WaitMined(ctx, c, tx.Hash()) + if iErr != nil || receipt == nil || receipt.Status != 1 { + return nil, false + } + return &addr, true } - receipt, err := bind.WaitMined(ctx, c, tx.Hash()) - if err != nil || receipt == nil || receipt.Status != 1 { - return nil, false + // fallback to dynamic deployment + addr, tx, _, err = Multicall3IndeterministicDeploy(c, tops) + if err == nil { + receipt, iErr := bind.WaitMined(ctx, c, tx.Hash()) + if iErr != nil || receipt == nil || receipt.Status != 1 { + return nil, false + } + + return &addr, true } - return &addr, true + return nil, false } func Multicall3MaxAccountsToFundPerTx(ctx context.Context, c *ethclient.Client) (uint64, error) {