github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/accounts/abi/bind/base.go (about) 1 // Copyright 2015 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package bind 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "math/big" 24 25 "github.com/SmartMeshFoundation/Spectrum" 26 "github.com/SmartMeshFoundation/Spectrum/accounts/abi" 27 "github.com/SmartMeshFoundation/Spectrum/common" 28 "github.com/SmartMeshFoundation/Spectrum/core/types" 29 "github.com/SmartMeshFoundation/Spectrum/crypto" 30 "github.com/SmartMeshFoundation/Spectrum/params" 31 ) 32 33 // SignerFn is a signer function callback when a contract requires a method to 34 // sign the transaction before submission. 35 type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) 36 37 type CallOptsWithNumber struct { 38 CallOpts 39 Number *big.Int // block number 40 Hash *common.Hash //block hash 41 } 42 43 // CallOpts is the collection of options to fine tune a contract call request. 44 type CallOpts struct { 45 Pending bool // Whether to operate on the pending state or the last known one 46 From common.Address // Optional the sender address, otherwise the first account is used 47 48 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 49 } 50 51 // TransactOpts is the collection of authorization data required to create a 52 // valid Ethereum transaction. 53 type TransactOpts struct { 54 From common.Address // Ethereum account to send the transaction from 55 Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) 56 Signer SignerFn // Method to use for signing the transaction (mandatory) 57 58 Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) 59 GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) 60 GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%) 61 62 Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) 63 } 64 65 // BoundContract is the base wrapper object that reflects a contract on the 66 // Ethereum network. It contains a collection of methods that are used by the 67 // higher level contract bindings to operate. 68 type BoundContract struct { 69 address common.Address // Deployment address of the contract on the Ethereum blockchain 70 abi abi.ABI // Reflect based ABI to access the correct Ethereum methods 71 caller ContractCaller // Read interface to interact with the blockchain 72 transactor ContractTransactor // Write interface to interact with the blockchain 73 } 74 75 // NewBoundContract creates a low level contract interface through which calls 76 // and transactions may be made through. 77 func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract { 78 return &BoundContract{ 79 address: address, 80 abi: abi, 81 caller: caller, 82 transactor: transactor, 83 } 84 } 85 86 // DeployContract deploys a contract onto the Ethereum blockchain and binds the 87 // deployment address with a Go wrapper. 88 func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { 89 // Otherwise try to deploy the contract 90 c := NewBoundContract(common.Address{}, abi, backend, backend) 91 92 input, err := c.abi.Pack("", params...) 93 if err != nil { 94 return common.Address{}, nil, nil, err 95 } 96 tx, err := c.transact(opts, nil, append(bytecode, input...)) 97 if err != nil { 98 return common.Address{}, nil, nil, err 99 } 100 c.address = crypto.CreateAddress(opts.From, tx.Nonce()) 101 return c.address, tx, c, nil 102 } 103 104 func (c *BoundContract) CallWithNumber(opts *CallOptsWithNumber, result interface{}, method string, params ...interface{}) error { 105 // Don't crash on a lazy user 106 if opts == nil { 107 opts = new(CallOptsWithNumber) 108 } 109 // Pack the input, call and unpack the results 110 input, err := c.abi.Pack(method, params...) 111 if err != nil { 112 return err 113 } 114 var ( 115 msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} 116 ctx = ensureContext(opts.Context) 117 code []byte 118 output []byte 119 ) 120 if opts.Pending { 121 pb, ok := c.caller.(PendingContractCaller) 122 if !ok { 123 return ErrNoPendingState 124 } 125 output, err = pb.PendingCallContract(ctx, msg) 126 if err == nil && len(output) == 0 { 127 // Make sure we have a contract to operate on, and bail out otherwise. 128 if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { 129 return err 130 } else if len(code) == 0 { 131 return ErrNoCode 132 } 133 } 134 } else { 135 //if opts.Hash == common.HexToHash("0x") { 136 if opts.Hash == nil { 137 output, err = c.caller.CallContract(ctx, msg, opts.Number) 138 } else { 139 output, err = c.caller.CallContractWithHash(ctx, msg, *opts.Hash) 140 } 141 if err == nil && len(output) == 0 { 142 // Make sure we have a contract to operate on, and bail out otherwise. 143 if code, err = c.caller.CodeAt(ctx, c.address, opts.Number); err != nil { 144 return err 145 } else if len(code) == 0 { 146 return ErrNoCode 147 } 148 } 149 } 150 if err != nil { 151 return err 152 } 153 return c.abi.Unpack(result, method, output) 154 } 155 156 // Call invokes the (constant) contract method with params as input values and 157 // sets the output to result. The result type might be a single field for simple 158 // returns, a slice of interfaces for anonymous returns and a struct for named 159 // returns. 160 func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { 161 // Don't crash on a lazy user 162 if opts == nil { 163 opts = new(CallOpts) 164 } 165 // Pack the input, call and unpack the results 166 input, err := c.abi.Pack(method, params...) 167 if err != nil { 168 return err 169 } 170 var ( 171 msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} 172 ctx = ensureContext(opts.Context) 173 code []byte 174 output []byte 175 ) 176 if opts.Pending { 177 pb, ok := c.caller.(PendingContractCaller) 178 if !ok { 179 return ErrNoPendingState 180 } 181 output, err = pb.PendingCallContract(ctx, msg) 182 if err == nil && len(output) == 0 { 183 // Make sure we have a contract to operate on, and bail out otherwise. 184 if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { 185 return err 186 } else if len(code) == 0 { 187 return ErrNoCode 188 } 189 } 190 } else { 191 output, err = c.caller.CallContract(ctx, msg, nil) 192 if err == nil && len(output) == 0 { 193 // Make sure we have a contract to operate on, and bail out otherwise. 194 if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil { 195 return err 196 } else if len(code) == 0 { 197 return ErrNoCode 198 } 199 } 200 } 201 if err != nil { 202 return err 203 } 204 return c.abi.Unpack(result, method, output) 205 } 206 207 // Transact invokes the (paid) contract method with params as input values. 208 func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 209 // Otherwise pack up the parameters and invoke the contract 210 input, err := c.abi.Pack(method, params...) 211 if err != nil { 212 return nil, err 213 } 214 return c.transact(opts, &c.address, input) 215 } 216 217 // Transfer initiates a plain transaction to move funds to the contract, calling 218 // its default method if one is available. 219 func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { 220 return c.transact(opts, &c.address, nil) 221 } 222 223 // transact executes an actual transaction invocation, first deriving any missing 224 // authorization fields, and then scheduling the transaction for execution. 225 func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { 226 var err error 227 228 // Ensure a valid value field and resolve the account nonce 229 value := opts.Value 230 if value == nil { 231 value = new(big.Int) 232 } 233 var nonce uint64 234 if opts.Nonce == nil { 235 nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) 236 if err != nil { 237 return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) 238 } 239 } else { 240 nonce = opts.Nonce.Uint64() 241 } 242 // Figure out the gas allowance and gas price values 243 gasPrice := opts.GasPrice 244 if gasPrice == nil { 245 gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) 246 if err != nil { 247 return nil, fmt.Errorf("failed to suggest gas price: %v", err) 248 } 249 } 250 gasLimit := opts.GasLimit 251 if gasLimit == nil { 252 // Gas estimation cannot succeed without code for method invocations 253 if contract != nil { 254 if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { 255 return nil, err 256 } else if len(code) == 0 { 257 return nil, ErrNoCode 258 } 259 } 260 // If the contract surely has code (or code is not needed), estimate the transaction 261 msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} 262 gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) 263 if err != nil { 264 return nil, fmt.Errorf("failed to estimate gas needed: %v", err) 265 } 266 } 267 // Create the transaction, sign it and schedule it for execution 268 var rawTx *types.Transaction 269 if contract == nil { 270 rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) 271 } else { 272 rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) 273 } 274 if opts.Signer == nil { 275 return nil, errors.New("no signer to authorize the transaction with") 276 } 277 // 18-09-13 : modify by liangc : change signer to eip155 278 //signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) 279 cid := params.MainnetChainConfig.ChainId 280 if params.IsTestnet() { 281 cid = params.TestnetChainConfig.ChainId 282 } else if params.IsDevnet() { 283 cid = params.DevnetChainConfig.ChainId 284 } 285 signedTx, err := opts.Signer(types.NewEIP155Signer(cid), opts.From, rawTx) 286 if err != nil { 287 return nil, err 288 } 289 if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { 290 return nil, err 291 } 292 return signedTx, nil 293 } 294 295 func ensureContext(ctx context.Context) context.Context { 296 if ctx == nil { 297 return context.TODO() 298 } 299 return ctx 300 }