From 66df1f26b86ab2c433c802c25e2a0534871511fb Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 16 Jul 2025 21:36:44 +0800 Subject: [PATCH] account/abi/bind/v2: fix TestDeploymentWithOverrides (#32212) The root cause of the flaky test was a nonce conflict caused by async contract deployments. This solution defines a custom deployer with automatic nonce management. --- accounts/abi/bind/v2/lib.go | 25 +++++++++++++++++++++++++ accounts/abi/bind/v2/lib_test.go | 12 ++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go index 3831161341..f2a49d6799 100644 --- a/accounts/abi/bind/v2/lib.go +++ b/accounts/abi/bind/v2/lib.go @@ -28,6 +28,7 @@ package bind import ( "errors" + "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" @@ -241,3 +242,27 @@ func DefaultDeployer(opts *TransactOpts, backend ContractBackend) DeployFn { return addr, tx, nil } } + +// DeployerWithNonceAssignment is basically identical to DefaultDeployer, +// but it additionally tracks the nonce to enable automatic assignment. +// +// This is especially useful when deploying multiple contracts +// from the same address — whether they are independent contracts +// or part of a dependency chain that must be deployed in order. +func DeployerWithNonceAssignment(opts *TransactOpts, backend ContractBackend) DeployFn { + var pendingNonce int64 + if opts.Nonce != nil { + pendingNonce = opts.Nonce.Int64() + } + return func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) { + if pendingNonce != 0 { + opts.Nonce = big.NewInt(pendingNonce) + } + addr, tx, err := DeployContract(opts, deployer, backend, input) + if err != nil { + return common.Address{}, nil, err + } + pendingNonce = int64(tx.Nonce() + 1) + return addr, tx, nil + } +} diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index fc895edad5..ee1db9cf86 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -65,6 +65,14 @@ func makeTestDeployer(backend simulated.Client) func(input, deployer []byte) (co return bind.DefaultDeployer(bind.NewKeyedTransactor(testKey, chainId), backend) } +// makeTestDeployerWithNonceAssignment is similar to makeTestDeployer, +// but it returns a deployer that automatically tracks nonce, +// enabling the deployment of multiple contracts from the same account. +func makeTestDeployerWithNonceAssignment(backend simulated.Client) func(input, deployer []byte) (common.Address, *types.Transaction, error) { + chainId, _ := backend.ChainID(context.Background()) + return bind.DeployerWithNonceAssignment(bind.NewKeyedTransactor(testKey, chainId), backend) +} + // test that deploying a contract with library dependencies works, // verifying by calling method on the deployed contract. func TestDeploymentLibraries(t *testing.T) { @@ -80,7 +88,7 @@ func TestDeploymentLibraries(t *testing.T) { Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, } - res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend.Client)) + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployerWithNonceAssignment(bindBackend.Client)) if err != nil { t.Fatalf("err: %+v\n", err) } @@ -122,7 +130,7 @@ func TestDeploymentWithOverrides(t *testing.T) { deploymentParams := &bind.DeploymentParams{ Contracts: nested_libraries.C1MetaData.Deps, } - res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployerWithNonceAssignment(bindBackend)) if err != nil { t.Fatalf("err: %+v\n", err) }