github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/accounts/abi/bind/base.go (about) 1 package bind 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/big" 8 9 "github.com/neatlab/neatio" 10 "github.com/neatlab/neatio/chain/accounts/abi" 11 "github.com/neatlab/neatio/chain/core/types" 12 "github.com/neatlab/neatio/utilities/common" 13 "github.com/neatlab/neatio/utilities/crypto" 14 "github.com/neatlab/neatio/utilities/event" 15 ) 16 17 type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) 18 19 type CallOpts struct { 20 Pending bool 21 From common.Address 22 BlockNumber *big.Int 23 Context context.Context 24 } 25 26 type TransactOpts struct { 27 From common.Address 28 Nonce *big.Int 29 Signer SignerFn 30 31 Value *big.Int 32 GasPrice *big.Int 33 GasLimit uint64 34 35 Context context.Context 36 } 37 38 type FilterOpts struct { 39 Start uint64 40 End *uint64 41 42 Context context.Context 43 } 44 45 type WatchOpts struct { 46 Start *uint64 47 Context context.Context 48 } 49 50 type BoundContract struct { 51 address common.Address 52 abi abi.ABI 53 caller ContractCaller 54 transactor ContractTransactor 55 filterer ContractFilterer 56 } 57 58 func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract { 59 return &BoundContract{ 60 address: address, 61 abi: abi, 62 caller: caller, 63 transactor: transactor, 64 filterer: filterer, 65 } 66 } 67 68 func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { 69 70 c := NewBoundContract(common.Address{}, abi, backend, backend, backend) 71 72 input, err := c.abi.Pack("", params...) 73 if err != nil { 74 return common.Address{}, nil, nil, err 75 } 76 tx, err := c.transact(opts, nil, append(bytecode, input...)) 77 if err != nil { 78 return common.Address{}, nil, nil, err 79 } 80 c.address = crypto.CreateAddress(opts.From, tx.Nonce()) 81 return c.address, tx, c, nil 82 } 83 84 func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { 85 86 if opts == nil { 87 opts = new(CallOpts) 88 } 89 90 input, err := c.abi.Pack(method, params...) 91 if err != nil { 92 return err 93 } 94 var ( 95 msg = neatio.CallMsg{From: opts.From, To: &c.address, Data: input} 96 ctx = ensureContext(opts.Context) 97 code []byte 98 output []byte 99 ) 100 if opts.Pending { 101 pb, ok := c.caller.(PendingContractCaller) 102 if !ok { 103 return ErrNoPendingState 104 } 105 output, err = pb.PendingCallContract(ctx, msg) 106 if err == nil && len(output) == 0 { 107 108 if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { 109 return err 110 } else if len(code) == 0 { 111 return ErrNoCode 112 } 113 } 114 } else { 115 output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber) 116 if err == nil && len(output) == 0 { 117 118 if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil { 119 return err 120 } else if len(code) == 0 { 121 return ErrNoCode 122 } 123 } 124 } 125 if err != nil { 126 return err 127 } 128 return c.abi.Unpack(result, method, output) 129 } 130 131 func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 132 133 input, err := c.abi.Pack(method, params...) 134 if err != nil { 135 return nil, err 136 } 137 return c.transact(opts, &c.address, input) 138 } 139 140 func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { 141 return c.transact(opts, &c.address, nil) 142 } 143 144 func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { 145 var err error 146 147 value := opts.Value 148 if value == nil { 149 value = new(big.Int) 150 } 151 var nonce uint64 152 if opts.Nonce == nil { 153 nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) 154 if err != nil { 155 return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) 156 } 157 } else { 158 nonce = opts.Nonce.Uint64() 159 } 160 161 gasPrice := opts.GasPrice 162 if gasPrice == nil { 163 gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) 164 if err != nil { 165 return nil, fmt.Errorf("failed to suggest gas price: %v", err) 166 } 167 } 168 gasLimit := opts.GasLimit 169 if gasLimit == 0 { 170 171 if contract != nil { 172 if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { 173 return nil, err 174 } else if len(code) == 0 { 175 return nil, ErrNoCode 176 } 177 } 178 179 msg := neatio.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input} 180 gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) 181 if err != nil { 182 return nil, fmt.Errorf("failed to estimate gas needed: %v", err) 183 } 184 } 185 186 var rawTx *types.Transaction 187 if contract == nil { 188 rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) 189 } else { 190 rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) 191 } 192 if opts.Signer == nil { 193 return nil, errors.New("no signer to authorize the transaction with") 194 } 195 signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) 196 if err != nil { 197 return nil, err 198 } 199 if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { 200 return nil, err 201 } 202 return signedTx, nil 203 } 204 205 func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 206 207 if opts == nil { 208 opts = new(FilterOpts) 209 } 210 211 query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...) 212 213 topics, err := makeTopics(query...) 214 if err != nil { 215 return nil, nil, err 216 } 217 218 logs := make(chan types.Log, 128) 219 220 config := neatio.FilterQuery{ 221 Addresses: []common.Address{c.address}, 222 Topics: topics, 223 FromBlock: new(big.Int).SetUint64(opts.Start), 224 } 225 if opts.End != nil { 226 config.ToBlock = new(big.Int).SetUint64(*opts.End) 227 } 228 229 buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config) 230 if err != nil { 231 return nil, nil, err 232 } 233 sub, err := event.NewSubscription(func(quit <-chan struct{}) error { 234 for _, log := range buff { 235 select { 236 case logs <- log: 237 case <-quit: 238 return nil 239 } 240 } 241 return nil 242 }), nil 243 244 if err != nil { 245 return nil, nil, err 246 } 247 return logs, sub, nil 248 } 249 250 func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { 251 252 if opts == nil { 253 opts = new(WatchOpts) 254 } 255 256 query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...) 257 258 topics, err := makeTopics(query...) 259 if err != nil { 260 return nil, nil, err 261 } 262 263 logs := make(chan types.Log, 128) 264 265 config := neatio.FilterQuery{ 266 Addresses: []common.Address{c.address}, 267 Topics: topics, 268 } 269 if opts.Start != nil { 270 config.FromBlock = new(big.Int).SetUint64(*opts.Start) 271 } 272 sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) 273 if err != nil { 274 return nil, nil, err 275 } 276 return logs, sub, nil 277 } 278 279 func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { 280 if len(log.Data) > 0 { 281 if err := c.abi.Unpack(out, event, log.Data); err != nil { 282 return err 283 } 284 } 285 var indexed abi.Arguments 286 for _, arg := range c.abi.Events[event].Inputs { 287 if arg.Indexed { 288 indexed = append(indexed, arg) 289 } 290 } 291 return parseTopics(out, indexed, log.Topics[1:]) 292 } 293 294 func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { 295 if len(log.Data) > 0 { 296 if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { 297 return err 298 } 299 } 300 var indexed abi.Arguments 301 for _, arg := range c.abi.Events[event].Inputs { 302 if arg.Indexed { 303 indexed = append(indexed, arg) 304 } 305 } 306 return parseTopicsIntoMap(out, indexed, log.Topics[1:]) 307 } 308 309 func ensureContext(ctx context.Context) context.Context { 310 if ctx == nil { 311 return context.TODO() 312 } 313 return ctx 314 }