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  }