github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/bind/v2/dep_tree.go (about) 1 // Copyright 2025 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package bind 18 19 import ( 20 "encoding/hex" 21 "fmt" 22 "maps" 23 "strings" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/core/types" 27 ) 28 29 // DeploymentParams contains parameters needed to deploy one or more contracts via LinkAndDeploy 30 type DeploymentParams struct { 31 // list of all contracts targeted for the deployment 32 Contracts []*MetaData 33 34 // optional map of ABI-encoded constructor inputs keyed by the MetaData.ID. 35 Inputs map[string][]byte 36 37 // optional map of override addresses for specifying already-deployed 38 // contracts. It is keyed by the MetaData.ID. 39 Overrides map[string]common.Address 40 } 41 42 // validate determines whether the contracts specified in the DeploymentParams 43 // instance have embedded deployer code in their provided MetaData instances. 44 func (d *DeploymentParams) validate() error { 45 for _, meta := range d.Contracts { 46 if meta.Bin == "" { 47 return fmt.Errorf("cannot deploy contract %s: deployer code missing from metadata", meta.ID) 48 } 49 } 50 return nil 51 } 52 53 // DeploymentResult contains information about the result of a pending 54 // deployment made by LinkAndDeploy. 55 type DeploymentResult struct { 56 // Map of contract MetaData.ID to pending deployment transaction 57 Txs map[string]*types.Transaction 58 59 // Map of contract MetaData.ID to the address where it will be deployed 60 Addresses map[string]common.Address 61 } 62 63 // DeployFn deploys a contract given a deployer and optional input. It returns 64 // the address and a pending transaction, or an error if the deployment failed. 65 type DeployFn func(input, deployer []byte) (common.Address, *types.Transaction, error) 66 67 // depTreeDeployer is responsible for taking a dependency, deploying-and-linking 68 // its components in the proper order. A depTreeDeployer cannot be used after 69 // calling LinkAndDeploy other than to retrieve the deployment result. 70 type depTreeDeployer struct { 71 deployedAddrs map[string]common.Address 72 deployerTxs map[string]*types.Transaction 73 inputs map[string][]byte // map of the root contract pattern to the constructor input (if there is any) 74 deployFn DeployFn 75 } 76 77 func newDepTreeDeployer(deployParams *DeploymentParams, deployFn DeployFn) *depTreeDeployer { 78 deployedAddrs := maps.Clone(deployParams.Overrides) 79 if deployedAddrs == nil { 80 deployedAddrs = make(map[string]common.Address) 81 } 82 inputs := deployParams.Inputs 83 if inputs == nil { 84 inputs = make(map[string][]byte) 85 } 86 return &depTreeDeployer{ 87 deployFn: deployFn, 88 deployedAddrs: deployedAddrs, 89 deployerTxs: make(map[string]*types.Transaction), 90 inputs: inputs, 91 } 92 } 93 94 // linkAndDeploy deploys a contract and it's dependencies. Because libraries 95 // can in-turn have their own library dependencies, linkAndDeploy performs 96 // deployment recursively (deepest-dependency first). The address of the 97 // pending contract deployment for the top-level contract is returned. 98 func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, error) { 99 // Don't re-deploy aliased or previously-deployed contracts 100 if addr, ok := d.deployedAddrs[metadata.ID]; ok { 101 return addr, nil 102 } 103 // If this contract/library depends on other libraries deploy them 104 // (and their dependencies) first 105 deployerCode := metadata.Bin 106 for _, dep := range metadata.Deps { 107 addr, err := d.linkAndDeploy(dep) 108 if err != nil { 109 return common.Address{}, err 110 } 111 // Link their deployed addresses into the bytecode to produce 112 deployerCode = strings.ReplaceAll(deployerCode, "__$"+dep.ID+"$__", strings.ToLower(addr.String()[2:])) 113 } 114 // Finally, deploy the top-level contract. 115 code, err := hex.DecodeString(deployerCode[2:]) 116 if err != nil { 117 panic(fmt.Sprintf("error decoding contract deployer hex %s:\n%v", deployerCode[2:], err)) 118 } 119 addr, tx, err := d.deployFn(d.inputs[metadata.ID], code) 120 if err != nil { 121 return common.Address{}, err 122 } 123 d.deployedAddrs[metadata.ID] = addr 124 d.deployerTxs[metadata.ID] = tx 125 return addr, nil 126 } 127 128 // result returns a DeploymentResult instance referencing contracts deployed 129 // and not including any overrides specified for this deployment. 130 func (d *depTreeDeployer) result() *DeploymentResult { 131 // filter the override addresses from the deployed address set. 132 for pattern := range d.deployedAddrs { 133 if _, ok := d.deployerTxs[pattern]; !ok { 134 delete(d.deployedAddrs, pattern) 135 } 136 } 137 return &DeploymentResult{ 138 Txs: d.deployerTxs, 139 Addresses: d.deployedAddrs, 140 } 141 } 142 143 // LinkAndDeploy performs the contract deployment specified by params using the 144 // provided DeployFn to create, sign and submit transactions. 145 // 146 // Contracts can depend on libraries, which in-turn can have their own library 147 // dependencies. Therefore, LinkAndDeploy performs the deployment recursively, 148 // starting with libraries (and contracts) that don't have dependencies, and 149 // progressing through the contracts that depend upon them. 150 // 151 // If an error is encountered, the returned DeploymentResult only contains 152 // entries for the contracts whose deployment submission succeeded. 153 // 154 // LinkAndDeploy performs creation and submission of creation transactions, 155 // but does not ensure that the contracts are included in the chain. 156 func LinkAndDeploy(params *DeploymentParams, deploy DeployFn) (*DeploymentResult, error) { 157 if err := params.validate(); err != nil { 158 return nil, err 159 } 160 deployer := newDepTreeDeployer(params, deploy) 161 for _, contract := range params.Contracts { 162 if _, err := deployer.linkAndDeploy(contract); err != nil { 163 return deployer.result(), err 164 } 165 } 166 return deployer.result(), nil 167 }