commit of wip-code: rework the contract deployment code to be cleaner
This commit is contained in:
parent
cd86fca55c
commit
0df73f3fac
|
@ -151,9 +151,10 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
|
|||
return c.address, tx, c, nil
|
||||
}
|
||||
|
||||
func DeployContractRaw(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, *BoundContract, error) {
|
||||
// Otherwise try to deploy the contract
|
||||
c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
|
||||
func DeployContractRaw(opts *TransactOpts, bytecode []byte, backend ContractBackend, packedParams []byte) (common.Address, *types.Transaction, *BoundContract, error) {
|
||||
// TODO: it's weird to instantiate a bound contract (implies existence of contract) in order to deploy a contract
|
||||
// that doesn't yet exist
|
||||
c := NewBoundContract(common.Address{}, abi.ABI{}, backend, backend, backend)
|
||||
|
||||
tx, err := c.transact(opts, nil, append(bytecode, packedParams...))
|
||||
if err != nil {
|
||||
|
|
|
@ -39,8 +39,8 @@ var (
|
|||
|
||||
{{range $contract := .Contracts}}
|
||||
var {{$contract.Type}}LibraryDeps = map[string]*bind.MetaData{
|
||||
{{range $pattern, $name := .AllLibraries -}}
|
||||
"{{$pattern}}": &{{$name}}MetaData,
|
||||
{{range $name, $pattern := .AllLibraries -}}
|
||||
"{{$pattern}}": {{$name}}MetaData,
|
||||
{{ end}}
|
||||
}
|
||||
|
||||
|
@ -58,18 +58,9 @@ var (
|
|||
{{end}}
|
||||
}
|
||||
|
||||
func (i *{{$contract.Type}}Instance) Address() common.Address {
|
||||
return i.address
|
||||
}
|
||||
|
||||
func (i *{{$contract.Type}}Instance) Backend() bind.ContractBackend {
|
||||
return i.backend
|
||||
}
|
||||
|
||||
// {{.Type}} is an auto generated Go binding around an Ethereum contract.
|
||||
type {{.Type}} struct {
|
||||
abi abi.ABI
|
||||
deployCode []byte
|
||||
}
|
||||
|
||||
// New{{.Type}} creates a new instance of {{.Type}}.
|
||||
|
@ -78,13 +69,10 @@ var (
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code := common.Hex2Bytes({{.Type}}MetaData.Bin)
|
||||
return &{{.Type}}{abi: *parsed, deployCode: code}, nil
|
||||
return &{{.Type}}{abi: *parsed}, nil
|
||||
}
|
||||
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}) DeployCode() []byte {
|
||||
return _{{$contract.Type}}.deployCode
|
||||
}
|
||||
// TODO: create custom exported types where unpack would generate a struct return.
|
||||
|
||||
// TODO: test constructor with inputs
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}) PackConstructor({{range .Constructor.Inputs}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ([]byte, error) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
|
@ -17,49 +18,101 @@ type ContractInstance struct {
|
|||
Backend bind.ContractBackend
|
||||
}
|
||||
|
||||
func DeployContracts(auth *bind.TransactOpts, backend bind.ContractBackend, constructorInput []byte, contracts map[string]*bind.MetaData) {
|
||||
// match if the contract has dynamic libraries that need to be linked
|
||||
hasDepsMatcher, err := regexp.Compile("__\\$.*\\$__")
|
||||
func deployDeps(backend bind.ContractBackend, auth *bind.TransactOpts, constructorInputs map[string][]byte, contracts map[string]string) (deploymentTxs map[common.Address]*types.Transaction, deployAddrs map[common.Address]struct{}, err error) {
|
||||
for pattern, contractBin := range contracts {
|
||||
contractBinBytes, err := hex.DecodeString(contractBin[2:])
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("contract bytecode is not a hex string: %s", contractBin[2:])
|
||||
}
|
||||
var constructorInput []byte
|
||||
if inp, ok := constructorInputs[pattern]; ok {
|
||||
constructorInput = inp
|
||||
} else {
|
||||
constructorInput = make([]byte, 0) // TODO check if we can pass a nil byte slice here.
|
||||
}
|
||||
addr, tx, _, err := bind.DeployContractRaw(auth, contractBinBytes, backend, constructorInput)
|
||||
if err != nil {
|
||||
return deploymentTxs, deployAddrs, fmt.Errorf("failed to deploy contract: %v", err)
|
||||
}
|
||||
deploymentTxs[addr] = tx
|
||||
deployAddrs[addr] = struct{}{}
|
||||
}
|
||||
|
||||
return deploymentTxs, deployAddrs, nil
|
||||
}
|
||||
|
||||
func linkDeps(deps *map[string]string, linked *map[string]common.Address) (deployableDeps map[string]string) {
|
||||
reMatchSpecificPattern, err := regexp.Compile("__\\$([a-f0-9]+)\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
reMatchAnyPattern, err := regexp.Compile("__\\$.*\\$__")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
deployableDeps = make(map[string]string)
|
||||
|
||||
// deps we are linking
|
||||
wipDeps := make(map[string]string)
|
||||
for id, metadata := range contracts {
|
||||
wipDeps[id] = metadata.Bin
|
||||
for pattern, dep := range *deps {
|
||||
// attempt to replace references to every single linked dep
|
||||
for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(dep, -1) {
|
||||
matchingPattern := match[1]
|
||||
addr, ok := (*linked)[matchingPattern]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
(*deps)[pattern] = strings.ReplaceAll(dep, matchingPattern, addr.String())
|
||||
}
|
||||
// if we linked something into this dep, see if it can be deployed
|
||||
if !reMatchAnyPattern.MatchString((*deps)[pattern]) {
|
||||
deployableDeps[pattern] = (*deps)[pattern]
|
||||
delete(*deps, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// nested iteration: find contracts without library dependencies first,
|
||||
// deploy them, link them into any other contracts that depend on them.
|
||||
// repeat this until there are no more contracts to link/deploy
|
||||
for {
|
||||
for id, contractBin := range wipDeps {
|
||||
if !hasDepsMatcher.MatchString(contractBin) {
|
||||
// this library/contract doesn't depend on any others
|
||||
// it can be deployed as-is.
|
||||
abi, err := contracts[id].GetAbi()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addr, _, _, err := bind.DeployContractRaw(auth, *abi, []byte(contractBin), backend, constructorInput)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
delete(wipDeps, id)
|
||||
return deployableDeps
|
||||
}
|
||||
|
||||
// embed the address of the deployed contract into any
|
||||
// libraries/contracts that depend on it.
|
||||
for id, contractBin := range wipDeps {
|
||||
contractBin = strings.ReplaceAll(contractBin, fmt.Sprintf("__$%s%__", id), fmt.Sprintf("__$%s$__", addr.String()))
|
||||
wipDeps[id] = contractBin
|
||||
}
|
||||
}
|
||||
func LinkAndDeployContractsWithOverride(auth *bind.TransactOpts, backend bind.ContractBackend, constructorInputs map[string][]byte, contracts, libs map[string]string, overrides map[string]common.Address) (allDeployTxs map[common.Address]*types.Transaction, allDeployAddrs map[common.Address]struct{}, err error) {
|
||||
var depsToDeploy map[string]string // map of pattern -> unlinked binary for deps we will deploy
|
||||
|
||||
// initialize the set of already-deployed contracts with given override addresses
|
||||
linked := make(map[string]common.Address)
|
||||
for pattern, deployAddr := range overrides {
|
||||
linked[pattern] = deployAddr
|
||||
if _, ok := contracts[pattern]; ok {
|
||||
delete(contracts, pattern)
|
||||
}
|
||||
if len(wipDeps) == 0 {
|
||||
}
|
||||
|
||||
// link and deploy dynamic libraries
|
||||
for {
|
||||
deployableDeps := linkDeps(&depsToDeploy, &linked)
|
||||
if len(deployableDeps) == 0 {
|
||||
break
|
||||
}
|
||||
deployTxs, deployAddrs, err := deployDeps(backend, auth, constructorInputs, deployableDeps)
|
||||
for addr, _ := range deployAddrs {
|
||||
allDeployAddrs[addr] = struct{}{}
|
||||
}
|
||||
for addr, tx := range deployTxs {
|
||||
allDeployTxs[addr] = tx
|
||||
}
|
||||
if err != nil {
|
||||
return deployTxs, allDeployAddrs, err
|
||||
}
|
||||
}
|
||||
|
||||
// link and deploy the contracts
|
||||
contractBins := make(map[string]string)
|
||||
linkedContracts := linkDeps(&contractBins, &linked)
|
||||
deployTxs, deployAddrs, err := deployDeps(backend, auth, constructorInputs, linkedContracts)
|
||||
for addr, _ := range deployAddrs {
|
||||
allDeployAddrs[addr] = struct{}{}
|
||||
}
|
||||
for addr, tx := range deployTxs {
|
||||
allDeployTxs[addr] = tx
|
||||
}
|
||||
return allDeployTxs, allDeployAddrs, err
|
||||
}
|
||||
|
||||
func FilterLogs[T any](instance *ContractInstance, opts *bind.FilterOpts, eventID common.Hash, unpack func(*types.Log) (*T, error), topics ...[]any) (*EventIterator[T], error) {
|
||||
|
|
|
@ -3,11 +3,15 @@ package v2
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/testdata/v2_generated_testcase"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/testdata/v2_testcase_library"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
|
@ -116,7 +120,61 @@ func TestV2(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeployment(t *testing.T) {
|
||||
DeployContracts
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
backend := simulated.NewBackend(
|
||||
types.GenesisAlloc{
|
||||
testAddr: {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
func(nodeConf *node.Config, ethConf *ethconfig.Config) {
|
||||
ethConf.Genesis.Difficulty = big.NewInt(0)
|
||||
},
|
||||
)
|
||||
defer backend.Close()
|
||||
|
||||
_, err := JSON(strings.NewReader(v2_generated_testcase.V2GeneratedTestcaseMetaData.ABI))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
signedTx, err := tx.WithSignature(signer, signature)
|
||||
if err != nil {
|
||||
t.Fatal(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...
|
||||
bindBackend := backends.SimulatedBackend{
|
||||
Backend: backend,
|
||||
Client: backend.Client(),
|
||||
}
|
||||
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stdout, log.LevelDebug, true)))
|
||||
|
||||
///LinkAndDeployContractsWithOverride(&opts, bindBackend, v2_test)
|
||||
deployTxs, err := DeployContracts(&opts, bindBackend, []byte{}, v2_testcase_library.TestArrayLibraryDeps)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %+v\n", err)
|
||||
}
|
||||
for _, tx := range deployTxs {
|
||||
fmt.Println("waiting for deployment")
|
||||
_, err = bind.WaitDeployed(context.Background(), &bindBackend, tx)
|
||||
if err != nil {
|
||||
t.Fatalf("error deploying bound contract: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* test-cases that should be extracted from v1 tests
|
||||
|
|
Loading…
Reference in New Issue