accounts/abi/bind/v2: modify v2 DeployContract to take packed input to be more in-line with the rest of the API. add a default DeployFn implementation for the deployer.

This commit is contained in:
Jared Wasinger 2025-02-04 19:18:25 -08:00
parent e8259bf98e
commit 6680b836f6
4 changed files with 79 additions and 55 deletions

View file

@ -227,7 +227,16 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller
}
func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
return bind2.DeployContract(opts, abi, bytecode, backend, params...)
packed, err := abi.Pack("", params...)
if err != nil {
return common.Address{}, nil, nil, err
}
addr, tx, err := bind2.DeployContract(opts, bytecode, backend, packed)
if err != nil {
return common.Address{}, nil, nil, err
}
boundContract := NewBoundContract(addr, abi, backend, backend, backend)
return addr, tx, boundContract, nil
}
// MetaData collects all metadata for a bound contract.

View file

@ -10,7 +10,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
)
@ -37,24 +36,6 @@ func NewBoundContractV1(address common.Address, abi abi.ABI, caller ContractCall
}
}
// DeployContract deploys a contract onto the Ethereum blockchain and binds the
// deployment address with a Go wrapper.
func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...any) (common.Address, *types.Transaction, *BoundContractV1, error) {
// Otherwise try to deploy the contract
c := NewBoundContractV1(common.Address{}, abi, backend, backend, backend)
input, err := c.abi.Pack("", params...)
if err != nil {
return common.Address{}, nil, nil, err
}
tx, err := c.transact(opts, nil, append(bytecode, input...))
if err != nil {
return common.Address{}, nil, nil, err
}
c.address = crypto.CreateAddress(opts.From, tx.Nonce())
return c.address, tx, c, nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named

View file

@ -17,6 +17,7 @@
package bind
import (
"context"
"errors"
"github.com/ethereum/go-ethereum"
@ -180,10 +181,10 @@ func Transact(c BoundContract, opt *TransactOpts, packedInput []byte) (*types.Tr
return c.transact(opt, &addr, packedInput)
}
// DeployContractRaw deploys a contract onto the Ethereum blockchain and binds the
// DeployContract deploys a contract onto the Ethereum blockchain and binds the
// deployment address with a Go wrapper. It expects its parameters to be abi-encoded
// bytes.
func DeployContractRaw(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, error) {
func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, error) {
c := NewBoundContractV1(common.Address{}, abi.ABI{}, backend, backend, backend)
tx, err := c.RawCreationTransact(opts, append(bytecode, packedParams...))
if err != nil {
@ -192,3 +193,20 @@ func DeployContractRaw(opts *TransactOpts, bytecode []byte, backend ContractBack
address := crypto.CreateAddress(opts.From, tx.Nonce())
return address, tx, nil
}
// DefaultDeployer returns a DeployFn that signs and submits creation transactions using the given signer.
func DefaultDeployer(ctx context.Context, from common.Address, backend ContractBackend, signer SignerFn) DeployFn {
opts := &TransactOpts{
From: from,
Nonce: nil,
Signer: signer,
Context: ctx,
}
return func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) {
addr, tx, err := DeployContract(opts, deployer, backend, input)
if err != nil {
return common.Address{}, nil, err
}
return addr, tx, nil
}
}

View file

@ -41,6 +41,7 @@ import (
)
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
var testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
// JSON returns a parsed ABI interface and error if it failed.
func JSON(reader io.Reader) (abi.ABI, error) {
@ -53,8 +54,7 @@ func JSON(reader io.Reader) (abi.ABI, error) {
return instance, nil
}
func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
func testSetup() (*backends.SimulatedBackend, error) {
backend := simulated.NewBackend(
types.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000000000)},
@ -64,23 +64,6 @@ func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) {
},
)
signer := types.LatestSigner(params.AllDevChainProtocolChanges)
opts := &bind.TransactOpts{
From: testAddr,
Nonce: nil,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
if err != nil {
return nil, err
}
signedTx, err := tx.WithSignature(signer, signature)
if err != nil {
return nil, err
}
return signedTx, nil
},
Context: context.Background(),
}
// we should just be able to use the backend directly, instead of using
// this deprecated interface. However, the simulated backend no longer
// implements backends.SimulatedBackend...
@ -88,19 +71,30 @@ func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) {
Backend: backend,
Client: backend.Client(),
}
return opts, &bindBackend, nil
return &bindBackend, nil
}
func makeTestDeployer(auth *bind.TransactOpts, backend bind.ContractBackend) func(input, deployer []byte) (common.Address, *types.Transaction, error) {
return func(input, deployer []byte) (common.Address, *types.Transaction, error) {
return bind.DeployContractRaw(auth, deployer, backend, input)
func makeTestDeployer(backend bind.ContractBackend) func(input, deployer []byte) (common.Address, *types.Transaction, error) {
signer := types.LatestSigner(params.AllDevChainProtocolChanges)
sign := func(sender common.Address, tx *types.Transaction) (*types.Transaction, error) {
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
if err != nil {
return nil, err
}
signedTx, err := tx.WithSignature(signer, signature)
if err != nil {
return nil, err
}
return signedTx, nil
}
return bind.DefaultDeployer(context.Background(), testAddr, backend, sign)
}
// test that deploying a contract with library dependencies works,
// verifying by calling method on the deployed contract.
func TestDeploymentLibraries(t *testing.T) {
opts, bindBackend, err := testSetup()
bindBackend, err := testSetup()
if err != nil {
t.Fatalf("err setting up test: %v", err)
}
@ -112,7 +106,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(opts, bindBackend))
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend))
if err != nil {
t.Fatalf("err: %+v\n", err)
}
@ -144,7 +138,7 @@ func TestDeploymentLibraries(t *testing.T) {
// Same as TestDeployment. However, stagger the deployments with overrides:
// first deploy the library deps and then the contract.
func TestDeploymentWithOverrides(t *testing.T) {
opts, bindBackend, err := testSetup()
bindBackend, err := testSetup()
if err != nil {
t.Fatalf("err setting up test: %v", err)
}
@ -154,7 +148,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
deploymentParams := &bind.DeploymentParams{
Contracts: nested_libraries.C1MetaData.Deps,
}
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend))
if err != nil {
t.Fatalf("err: %+v\n", err)
}
@ -180,7 +174,7 @@ func TestDeploymentWithOverrides(t *testing.T) {
Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput},
Overrides: overrides,
}
res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend))
res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend))
if err != nil {
t.Fatalf("err: %+v\n", err)
}
@ -209,15 +203,37 @@ func TestDeploymentWithOverrides(t *testing.T) {
}
}
// returns transaction auth to send a basic transaction from testAddr
func defaultTxAuth() *bind.TransactOpts {
signer := types.LatestSigner(params.AllDevChainProtocolChanges)
opts := &bind.TransactOpts{
From: testAddr,
Nonce: nil,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
if err != nil {
return nil, err
}
signedTx, err := tx.WithSignature(signer, signature)
if err != nil {
return nil, err
}
return signedTx, nil
},
Context: context.Background(),
}
return opts
}
func TestEvents(t *testing.T) {
// test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.)
txAuth, backend, err := testSetup()
backend, err := testSetup()
if err != nil {
t.Fatalf("error setting up testing env: %v", err)
}
deploymentParams := &bind.DeploymentParams{Contracts: []*bind.MetaData{&events.CMetaData}}
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
if err != nil {
t.Fatalf("error deploying contract for testing: %v", err)
}
@ -245,7 +261,7 @@ func TestEvents(t *testing.T) {
defer sub2.Unsubscribe()
packedInput := c.PackEmitMulti()
tx, err := bind.Transact(instance, txAuth, packedInput)
tx, err := bind.Transact(instance, defaultTxAuth(), packedInput)
if err != nil {
t.Fatalf("failed to send transaction: %v", err)
}
@ -310,13 +326,13 @@ done:
func TestErrors(t *testing.T) {
// test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.)
txAuth, backend, err := testSetup()
backend, err := testSetup()
if err != nil {
t.Fatalf("error setting up testing env: %v", err)
}
deploymentParams := &bind.DeploymentParams{Contracts: []*bind.MetaData{&solc_errors.CMetaData}}
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend))
res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
if err != nil {
t.Fatalf("error deploying contract for testing: %v", err)
}