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) { 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. // 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/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event" "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 // 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 // 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 // returns, a slice of interfaces for anonymous returns and a struct for named

View file

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