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 }