Skip to content

Commit d47b7f4

Browse files
committed
fund: add ERC20 transfer via multicall3
1 parent d5c398e commit d47b7f4

File tree

3 files changed

+189
-74
lines changed

3 files changed

+189
-74
lines changed

cmd/fund/fund.go

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -81,46 +81,33 @@ func runFunding(ctx context.Context) error {
8181
}()
8282
}
8383

84-
// If ERC20 mode is enabled, fund with tokens instead of ETH
85-
if params.TokenAddress != "" {
86-
log.Info().Str("tokenAddress", params.TokenAddress).Msg("Starting ERC20 token funding (ETH funding disabled)")
87-
if err = fundWalletsWithERC20(ctx, c, tops, privateKey, addresses, privateKeys); err != nil {
88-
return err
89-
}
90-
log.Info().Msg("Wallet(s) funded with ERC20 tokens! 🪙")
91-
} else {
92-
// Fund wallets.
93-
log.Debug().Msg("checking if multicall3 is supported")
94-
var multicall3Addr *common.Address
95-
if len(params.Multicall3Address) > 0 {
96-
addr := common.HexToAddress(params.Multicall3Address)
97-
if addr == (common.Address{}) {
98-
log.Warn().
99-
Str("address", params.Multicall3Address).
100-
Msg("invalid multicall3 address provided, will try to detect or deploy multicall3")
101-
multicall3Addr = &addr
102-
}
84+
// Fund wallets.
85+
log.Debug().Msg("checking if multicall3 is supported")
86+
var multicall3Addr *common.Address
87+
if len(params.Multicall3Address) > 0 {
88+
addr := common.HexToAddress(params.Multicall3Address)
89+
if addr == (common.Address{}) {
90+
log.Warn().
91+
Str("address", params.Multicall3Address).
92+
Msg("invalid multicall3 address provided, will try to detect or deploy multicall3")
93+
multicall3Addr = &addr
10394
}
95+
}
10496

105-
multicall3Addr, _ = util.IsMulticall3Supported(ctx, c, true, tops, multicall3Addr)
106-
if multicall3Addr != nil {
107-
log.Info().
108-
Stringer("address", multicall3Addr).
109-
Msg("multicall3 is supported and will be used to fund wallets")
110-
return fundWalletsWithMulticall3(ctx, c, tops, addresses, multicall3Addr)
111-
} else {
112-
log.Info().Msg("multicall3 is not supported, will use funder contract to fund wallets")
113-
// Deploy or instantiate the Funder contract.
114-
var contract *funder.Funder
115-
contract, err = deployOrInstantiateFunderContract(ctx, c, tops, privateKey, len(addresses))
116-
if err != nil {
117-
return err
118-
}
119-
if err = fundWallets(ctx, c, tops, contract, addresses); err != nil {
120-
return err
121-
}
122-
}
97+
multicall3Addr, _ = util.IsMulticall3Supported(ctx, c, true, tops, multicall3Addr)
98+
if multicall3Addr != nil {
99+
log.Info().
100+
Stringer("address", multicall3Addr).
101+
Msg("multicall3 is supported and will be used to fund wallets")
102+
err = fundWalletsWithMulticall3(ctx, c, tops, addresses, multicall3Addr)
103+
} else {
104+
log.Info().Msg("multicall3 is not supported, will use funder contract to fund wallets")
105+
err = fundWalletsWithFunder(ctx, c, tops, privateKey, addresses, privateKeys)
123106
}
107+
if err != nil {
108+
return err
109+
}
110+
124111
log.Info().Msg("Wallet(s) funded! 💸")
125112

126113
log.Info().Msgf("Total execution time: %s", time.Since(startTime))
@@ -355,6 +342,29 @@ func fundWallets(ctx context.Context, c *ethclient.Client, tops *bind.TransactOp
355342
return nil
356343
}
357344

345+
func fundWalletsWithFunder(ctx context.Context, c *ethclient.Client, tops *bind.TransactOpts, privateKey *ecdsa.PrivateKey, addresses []common.Address, privateKeys []*ecdsa.PrivateKey) error {
346+
var err error
347+
// If ERC20 mode is enabled, fund with tokens instead of ETH
348+
if params.TokenAddress != "" {
349+
log.Info().Str("tokenAddress", params.TokenAddress).Msg("Starting ERC20 token funding (ETH funding disabled)")
350+
if err = fundWalletsWithERC20(ctx, c, tops, privateKey, addresses, privateKeys); err != nil {
351+
return err
352+
}
353+
log.Info().Msg("Wallet(s) funded with ERC20 tokens! 🪙")
354+
} else {
355+
// Deploy or instantiate the Funder contract.
356+
var contract *funder.Funder
357+
contract, err = deployOrInstantiateFunderContract(ctx, c, tops, privateKey, len(addresses))
358+
if err != nil {
359+
return err
360+
}
361+
if err = fundWallets(ctx, c, tops, contract, addresses); err != nil {
362+
return err
363+
}
364+
}
365+
return nil
366+
}
367+
358368
func fundWalletsWithMulticall3(ctx context.Context, c *ethclient.Client, tops *bind.TransactOpts, wallets []common.Address, multicall3Addr *common.Address) error {
359369
log.Debug().
360370
Msg("funding wallets with multicall3")
@@ -370,7 +380,14 @@ func fundWalletsWithMulticall3(ctx context.Context, c *ethclient.Client, tops *b
370380
log.Debug().Uint64("accsToFundPerTx", accsToFundPerTx).Msg("multicall3 max accounts to fund per tx")
371381
chSize := (uint64(len(wallets)) / accsToFundPerTx) + 1
372382

373-
txsCh := make(chan *types.Transaction, chSize)
383+
var txsCh chan *types.Transaction
384+
if params.TokenAddress == "" {
385+
txsCh = make(chan *types.Transaction, chSize)
386+
} else {
387+
txsCh = make(chan *types.Transaction, chSize*2)
388+
}
389+
390+
errCh := make(chan error, chSize)
374391

375392
accs := []common.Address{}
376393
wg := sync.WaitGroup{}
@@ -390,16 +407,33 @@ func fundWalletsWithMulticall3(ctx context.Context, c *ethclient.Client, tops *b
390407
wg.Add(1)
391408
go func(tops *bind.TransactOpts, accs []common.Address) {
392409
defer wg.Done()
410+
var iErr error
393411
if rl != nil {
394-
err := rl.Wait(ctx)
395-
if err != nil {
396-
log.Error().Err(err).Msg("rate limiter wait failed before funding accounts with multicall3")
412+
iErr = rl.Wait(ctx)
413+
if iErr != nil {
414+
log.Error().Err(iErr).Msg("rate limiter wait failed before funding accounts with multicall3")
397415
return
398416
}
399417
}
400-
tx, err := util.Multicall3FundAccountsWithNativeToken(c, tops, accs, params.FundingAmountInWei, multicall3Addr)
401-
if err != nil {
402-
log.Error().Err(err).Msg("failed to fund accounts with multicall3")
418+
var tx *types.Transaction
419+
if params.TokenAddress != "" {
420+
tokenAddress := common.HexToAddress(params.TokenAddress)
421+
var txApprove *types.Transaction
422+
txApprove, tx, iErr = util.Multicall3FundAccountsWithERC20Token(ctx, c, tops, accs, tokenAddress, params.TokenAmount, multicall3Addr)
423+
if txApprove != nil {
424+
log.Info().
425+
Stringer("txHash", txApprove.Hash()).
426+
Int("done", i+1).
427+
Uint64("of", uint64(len(wallets))).
428+
Msg("transaction to approve ERC20 token spending by multicall3 sent")
429+
txsCh <- txApprove
430+
}
431+
} else {
432+
tx, iErr = util.Multicall3FundAccountsWithNativeToken(c, tops, accs, params.FundingAmountInWei, multicall3Addr)
433+
}
434+
if iErr != nil {
435+
errCh <- iErr
436+
log.Error().Err(iErr).Msg("failed to fund accounts with multicall3")
403437
return
404438
}
405439
log.Info().
@@ -414,6 +448,21 @@ func fundWalletsWithMulticall3(ctx context.Context, c *ethclient.Client, tops *b
414448
}
415449
wg.Wait()
416450
close(txsCh)
451+
close(errCh)
452+
453+
var combinedErrors error
454+
for len(errCh) > 0 {
455+
err = <-errCh
456+
if combinedErrors == nil {
457+
combinedErrors = err
458+
} else {
459+
combinedErrors = errors.Join(combinedErrors, err)
460+
}
461+
}
462+
// return if there were errors sending the funding transactions
463+
if combinedErrors != nil {
464+
return combinedErrors
465+
}
417466

418467
log.Info().Msg("all funding transactions sent, waiting for confirmation...")
419468

@@ -438,7 +487,7 @@ func fundWalletsWithMulticall3(ctx context.Context, c *ethclient.Client, tops *b
438487
}
439488
log.Info().
440489
Stringer("txHash", tx.Hash()).
441-
Msg("transaction to fund accounts confirmed")
490+
Msg("transaction confirmed")
442491
}
443492

444493
return nil

cmd/loadtest/account.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bin
485485
chSize := (uint64(len(ap.accounts)) / accsToFundPerTx) + 1
486486

487487
txsCh := make(chan *types.Transaction, chSize)
488+
errCh := make(chan error, chSize)
488489

489490
accs := []common.Address{}
490491
wg := sync.WaitGroup{}
@@ -495,25 +496,26 @@ func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bin
495496
if accountToFund.address == tops.From {
496497
continue
497498
}
498-
if mustBeFunded, err := ap.accountMustBeFunded(ctx, accountToFund); err != nil || !mustBeFunded {
499+
if mustBeFunded, iErr := ap.accountMustBeFunded(ctx, accountToFund); iErr != nil || !mustBeFunded {
499500
continue
500501
}
502+
501503
accs = append(accs, accountToFund.address)
502504

503505
if uint64(len(accs)) == accsToFundPerTx || i == len(ap.accounts)-1 {
504506
wg.Add(1)
505507
go func(tops *bind.TransactOpts, accs []common.Address) {
506508
defer wg.Done()
507-
err := ap.clientRateLimiter.Wait(ctx)
508-
if err != nil {
509-
log.Error().Err(err).Msg("rate limiter wait failed before funding accounts with multicall3")
509+
iErr := ap.clientRateLimiter.Wait(ctx)
510+
if iErr != nil {
511+
log.Error().Err(iErr).Msg("rate limiter wait failed before funding accounts with multicall3")
510512
return
511513
}
512514
mu.Lock()
513515
defer mu.Unlock()
514-
tx, err := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, ap.multicall3Addr)
515-
if err != nil {
516-
log.Error().Err(err).Msg("failed to fund accounts with multicall3")
516+
tx, iErr := util.Multicall3FundAccountsWithNativeToken(ap.client, tops, accs, ap.fundingAmount, ap.multicall3Addr)
517+
if iErr != nil {
518+
log.Error().Err(iErr).Msg("failed to fund accounts with multicall3")
517519
return
518520
}
519521
log.Info().
@@ -528,6 +530,21 @@ func (ap *AccountPool) fundAccountsWithMulticall3(ctx context.Context, tops *bin
528530
}
529531
wg.Wait()
530532
close(txsCh)
533+
close(errCh)
534+
535+
var combinedErrors error
536+
for len(errCh) > 0 {
537+
err = <-errCh
538+
if combinedErrors == nil {
539+
combinedErrors = err
540+
} else {
541+
combinedErrors = errors.Join(combinedErrors, err)
542+
}
543+
}
544+
// return if there were errors sending the funding transactions
545+
if combinedErrors != nil {
546+
return combinedErrors
547+
}
531548

532549
log.Info().Msg("all funding transactions sent, waiting for confirmation...")
533550

0 commit comments

Comments
 (0)