Skip to content

Commit 4f86591

Browse files
authored
add multicall3 deterministic deploy logic; deploy multicall3 only when needed (#780)
1 parent 417a448 commit 4f86591

File tree

2 files changed

+96
-36
lines changed

2 files changed

+96
-36
lines changed

cmd/loadtest/account.go

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ type AccountPool struct {
117117

118118
latestBlockNumber uint64
119119
pendingTxsCache *uint64
120-
121-
multicall3Addr *common.Address
122120
}
123121

124122
// Creates a new account pool with the given funding private key.
@@ -165,24 +163,6 @@ func NewAccountPool(ctx context.Context, client *ethclient.Client, fundingPrivat
165163
return nil, fmt.Errorf("unable to get latestBlockNumber: %w", err)
166164
}
167165

168-
tops, err := bind.NewKeyedTransactorWithChainID(fundingPrivateKey, chainID)
169-
if err != nil {
170-
log.Error().
171-
Err(err).
172-
Msg("unable create transaction signer")
173-
return nil, fmt.Errorf("unable create transaction signer: %w", err)
174-
}
175-
176-
log.Debug().Msg("checking if multicall3 is supported")
177-
multicall3Addr, _ := util.IsMulticall3Supported(ctx, client, true, tops, nil)
178-
if multicall3Addr != nil {
179-
log.Info().
180-
Stringer("address", multicall3Addr).
181-
Msg("multicall3 is supported and will be used to fund accounts")
182-
} else {
183-
log.Info().Msg("multicall3 is not supported, will use EOA transfers to fund accounts")
184-
}
185-
186166
ap := &AccountPool{
187167
currentAccountIndex: 0,
188168
client: client,
@@ -193,7 +173,6 @@ func NewAccountPool(ctx context.Context, client *ethclient.Client, fundingPrivat
193173
accountsPositions: make(map[common.Address]int),
194174
latestBlockNumber: latestBlockNumber,
195175
clientRateLimiter: rate.NewLimiter(rate.Every(50*time.Millisecond), 1),
196-
multicall3Addr: multicall3Addr,
197176
}
198177

199178
if !ap.isFundingEnabled() {
@@ -463,13 +442,23 @@ func (ap *AccountPool) FundAccounts(ctx context.Context) error {
463442
return errors.New(errMsg)
464443
}
465444

466-
if ap.multicall3Addr != nil {
467-
return ap.fundAccountsWithMulticall3(ctx, tops)
445+
log.Debug().Msg("checking if multicall3 is supported")
446+
multicall3Addr, _ := util.IsMulticall3Supported(ctx, ap.client, true, tops, nil)
447+
if multicall3Addr != nil {
448+
log.Info().
449+
Stringer("address", multicall3Addr).
450+
Msg("multicall3 is supported and will be used to fund accounts")
451+
} else {
452+
log.Info().Msg("multicall3 is not supported, will use EOA transfers to fund accounts")
453+
}
454+
455+
if multicall3Addr != nil {
456+
return ap.fundAccountsWithMulticall3(ctx, tops, multicall3Addr)
468457
}
469458
return ap.fundAccountsWithEOATransfers(ctx, tops)
470459
}
471460

472-
func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bind.TransactOpts) error {
461+
func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bind.TransactOpts, multicall3Addr *common.Address) error {
473462
log.Debug().
474463
Msg("funding sending accounts with multicall3")
475464

@@ -513,7 +502,7 @@ func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bin
513502
}
514503
mu.Lock()
515504
defer mu.Unlock()
516-
tx, iErr := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, ap.multicall3Addr)
505+
tx, iErr := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, multicall3Addr)
517506
if iErr != nil {
518507
log.Error().Err(iErr).Msg("failed to fund accounts with multicall3")
519508
return

util/multicall3.go

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import (
1313
"github.com/rs/zerolog/log"
1414
)
1515

16-
const DefaultMulticall3Addr = "0xcA11bde05977b3631167028862bE2a173976CA11"
16+
const (
17+
Multicall3SCAddr = "0xcA11bde05977b3631167028862bE2a173976CA11"
18+
Multicall3DeployerAddress = "0x05f32b3cc3888453ff71b01135b34ff8e41263f2"
19+
Multicall3SignedDeployTX = "0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086"
20+
)
1721

1822
// Contract CALL (Multicall3) must pay all CALL-path costs, including:
1923
// New account surcharge: 25,000 gas when value creates a previously empty account.
@@ -27,18 +31,74 @@ const DefaultMulticall3Addr = "0xcA11bde05977b3631167028862bE2a173976CA11"
2731
const estimatedGasNeededToFundASingleAccount = 40000 // estimated gas per account to fund with multicall3 + margin(3k)
2832
const maxAccsToFundPerTx = 700 // arbitrary limit to avoid too large transactions
2933

30-
func Multicall3Deploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) {
34+
func Multicall3IndeterministicDeploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) {
3135
address, tx, instance, err := multicall3.DeployMulticall3(sender, c)
3236
if err != nil {
3337
return common.Address{}, nil, nil, err
3438
}
3539
return address, tx, instance, nil
3640
}
3741

42+
func Multicall3DeterministicDeploy(c *ethclient.Client, sender *bind.TransactOpts) (common.Address, *types.Transaction, *multicall3.Multicall3, error) {
43+
deployerAddr := common.HexToAddress(Multicall3DeployerAddress)
44+
45+
// check deployer balance
46+
balance, err := c.BalanceAt(context.Background(), deployerAddr, nil)
47+
if err != nil {
48+
return common.Address{}, nil, nil, err
49+
}
50+
51+
gasLimit := uint64(1000000)
52+
gasPrice := big.NewInt(100000000000) // 100 Gwei
53+
maxDeployCost := big.NewInt(0).Mul(big.NewInt(int64(gasLimit)), gasPrice) // 0.1 ETH
54+
if balance.Cmp(maxDeployCost) < 0 {
55+
// fund deployer
56+
nonce, iErr := c.PendingNonceAt(context.Background(), sender.From)
57+
if iErr != nil {
58+
return common.Address{}, nil, nil, iErr
59+
}
60+
suggestedGasprice, iErr := c.SuggestGasPrice(context.Background())
61+
if iErr != nil {
62+
return common.Address{}, nil, nil, iErr
63+
}
64+
txFund := types.NewTransaction(nonce, deployerAddr, maxDeployCost, 21000, suggestedGasprice, nil)
65+
signedTxFund, iErr := sender.Signer(sender.From, txFund)
66+
if iErr != nil {
67+
return common.Address{}, nil, nil, iErr
68+
}
69+
if iErr = c.SendTransaction(context.Background(), signedTxFund); iErr != nil {
70+
return common.Address{}, nil, nil, iErr
71+
}
72+
receipt, iErr := bind.WaitMined(context.Background(), c, signedTxFund.Hash())
73+
if iErr != nil || receipt == nil || receipt.Status != 1 {
74+
return common.Address{}, nil, nil, iErr
75+
}
76+
}
77+
78+
rawTxBytes := common.FromHex(Multicall3SignedDeployTX)
79+
tx := new(types.Transaction)
80+
err = tx.UnmarshalBinary(rawTxBytes)
81+
if err != nil {
82+
return common.Address{}, nil, nil, err
83+
}
84+
85+
err = c.SendTransaction(context.Background(), tx)
86+
if err != nil {
87+
return common.Address{}, nil, nil, err
88+
}
89+
90+
address := common.HexToAddress(Multicall3SCAddr)
91+
instance, err := multicall3.NewMulticall3(address, c)
92+
if err != nil {
93+
return common.Address{}, nil, nil, err
94+
}
95+
return address, tx, instance, nil
96+
}
97+
3898
func Multicall3Exists(ctx context.Context, c *ethclient.Client, customAddr *common.Address) (common.Address, bool, error) {
3999
scAddr := customAddr
40100
if scAddr == nil {
41-
addr := common.HexToAddress(DefaultMulticall3Addr)
101+
addr := common.HexToAddress(Multicall3SCAddr)
42102
scAddr = &addr
43103
}
44104

@@ -67,7 +127,7 @@ func Multicall3Exists(ctx context.Context, c *ethclient.Client, customAddr *comm
67127
func Multicall3New(c *ethclient.Client, customAddr *common.Address) (common.Address, *multicall3.Multicall3, error) {
68128
scAddr := customAddr
69129
if scAddr == nil {
70-
addr := common.HexToAddress(DefaultMulticall3Addr)
130+
addr := common.HexToAddress(Multicall3SCAddr)
71131
scAddr = &addr
72132
}
73133

@@ -97,17 +157,28 @@ func IsMulticall3Supported(ctx context.Context, c *ethclient.Client, tryDeployIf
97157
return nil, false
98158
}
99159

100-
addr, tx, _, err := Multicall3Deploy(c, tops)
101-
if err != nil {
102-
return nil, false
160+
// try to deploy it deterministically first
161+
addr, tx, _, err := Multicall3DeterministicDeploy(c, tops)
162+
if err == nil {
163+
receipt, iErr := bind.WaitMined(ctx, c, tx.Hash())
164+
if iErr != nil || receipt == nil || receipt.Status != 1 {
165+
return nil, false
166+
}
167+
return &addr, true
103168
}
104169

105-
receipt, err := bind.WaitMined(ctx, c, tx.Hash())
106-
if err != nil || receipt == nil || receipt.Status != 1 {
107-
return nil, false
170+
// fallback to dynamic deployment
171+
addr, tx, _, err = Multicall3IndeterministicDeploy(c, tops)
172+
if err == nil {
173+
receipt, iErr := bind.WaitMined(ctx, c, tx.Hash())
174+
if iErr != nil || receipt == nil || receipt.Status != 1 {
175+
return nil, false
176+
}
177+
178+
return &addr, true
108179
}
109180

110-
return &addr, true
181+
return nil, false
111182
}
112183

113184
func Multicall3MaxAccountsToFundPerTx(ctx context.Context, c *ethclient.Client) (uint64, error) {

0 commit comments

Comments
 (0)