github.com/ChainSafe/chainbridge-core@v1.4.2/chains/evm/calls/contracts/contract.go (about)

     1  package contracts
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/ChainSafe/chainbridge-core/chains/evm/calls"
     7  	"github.com/ChainSafe/chainbridge-core/chains/evm/calls/transactor"
     8  	"github.com/ethereum/go-ethereum"
     9  	"github.com/ethereum/go-ethereum/accounts/abi"
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/crypto"
    12  	"github.com/rs/zerolog/log"
    13  )
    14  
    15  const DefaultDeployGasLimit = 6000000
    16  
    17  type Contract struct {
    18  	contractAddress common.Address
    19  	ABI             abi.ABI
    20  	bytecode        []byte
    21  	client          calls.ContractCallerDispatcher
    22  	transactor.Transactor
    23  }
    24  
    25  func NewContract(
    26  	contractAddress common.Address,
    27  	abi abi.ABI,
    28  	bytecode []byte,
    29  	client calls.ContractCallerDispatcher,
    30  	transactor transactor.Transactor,
    31  ) Contract {
    32  	return Contract{
    33  		contractAddress: contractAddress,
    34  		ABI:             abi,
    35  		bytecode:        bytecode,
    36  		client:          client,
    37  		Transactor:      transactor,
    38  	}
    39  }
    40  
    41  func (c *Contract) ContractAddress() *common.Address {
    42  	return &c.contractAddress
    43  }
    44  
    45  func (c *Contract) PackMethod(method string, args ...interface{}) ([]byte, error) {
    46  	input, err := c.ABI.Pack(method, args...)
    47  	if err != nil {
    48  		log.Error().Err(fmt.Errorf("pack method error: %v", err))
    49  		return []byte{}, err
    50  	}
    51  	return input, nil
    52  }
    53  
    54  func (c *Contract) UnpackResult(method string, output []byte) ([]interface{}, error) {
    55  	res, err := c.ABI.Unpack(method, output)
    56  	if err != nil {
    57  		log.Error().Err(fmt.Errorf("unpack output error: %v", err))
    58  		return nil, err
    59  	}
    60  	return res, err
    61  }
    62  
    63  func (c *Contract) ExecuteTransaction(method string, opts transactor.TransactOptions, args ...interface{}) (*common.Hash, error) {
    64  	input, err := c.PackMethod(method, args...)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	h, err := c.Transact(&c.contractAddress, input, opts)
    69  	if err != nil {
    70  		log.Error().
    71  			Str("contract", c.contractAddress.String()).
    72  			Err(err).
    73  			Msgf("error on executing %s", method)
    74  		return nil, err
    75  	}
    76  	log.Debug().
    77  		Str("txHash", h.String()).
    78  		Str("contract", c.contractAddress.String()).
    79  		Msgf("method %s executed", method)
    80  	return h, err
    81  }
    82  
    83  func (c *Contract) CallContract(method string, args ...interface{}) ([]interface{}, error) {
    84  	input, err := c.PackMethod(method, args...)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	msg := ethereum.CallMsg{From: c.client.From(), To: &c.contractAddress, Data: input}
    89  	out, err := c.client.CallContract(context.TODO(), calls.ToCallArg(msg), nil)
    90  	if err != nil {
    91  		log.Error().
    92  			Str("contract", c.contractAddress.String()).
    93  			Err(err).
    94  			Msgf("error on calling %s", method)
    95  		return nil, err
    96  	}
    97  	if len(out) == 0 {
    98  		// Make sure we have a contract to operate on, and bail out otherwise.
    99  		if code, err := c.client.CodeAt(context.Background(), c.contractAddress, nil); err != nil {
   100  			return nil, err
   101  		} else if len(code) == 0 {
   102  			return nil, fmt.Errorf("no code at provided address %s", c.contractAddress.String())
   103  		}
   104  	}
   105  	log.Debug().
   106  		Str("contract", c.contractAddress.String()).
   107  		Msgf("method %s called", method)
   108  	return c.UnpackResult(method, out)
   109  }
   110  
   111  func (c *Contract) DeployContract(params ...interface{}) (common.Address, error) {
   112  	input, err := c.PackMethod("", params...)
   113  	if err != nil {
   114  		return common.Address{}, err
   115  	}
   116  	opts := transactor.TransactOptions{GasLimit: DefaultDeployGasLimit}
   117  	hash, err := c.Transact(nil, append(c.bytecode, input...), opts)
   118  	if err != nil {
   119  		return common.Address{}, err
   120  	}
   121  	tx, _, err := c.client.GetTransactionByHash(*hash)
   122  	if err != nil {
   123  		return common.Address{}, err
   124  	}
   125  	address := crypto.CreateAddress(c.client.From(), tx.Nonce())
   126  	c.contractAddress = address
   127  	log.Debug().
   128  		Str("txHash", hash.String()).
   129  		Str("deployedAddress", address.String()).
   130  		Msgf("successful contract deployment")
   131  	return address, nil
   132  }