github.com/benorgera/go-ethereum@v1.10.18-0.20220401011646-b3f57b1a73ba/accounts/abi/bind/base_test.go (about) 1 // Copyright 2019 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_test 18 19 import ( 20 "context" 21 "math/big" 22 "reflect" 23 "strings" 24 "testing" 25 26 "github.com/ethereum/go-ethereum" 27 "github.com/ethereum/go-ethereum/accounts/abi" 28 "github.com/ethereum/go-ethereum/accounts/abi/bind" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/common/hexutil" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/rlp" 34 "github.com/stretchr/testify/assert" 35 ) 36 37 func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil } 38 39 type mockTransactor struct { 40 baseFee *big.Int 41 gasTipCap *big.Int 42 gasPrice *big.Int 43 suggestGasTipCapCalled bool 44 suggestGasPriceCalled bool 45 } 46 47 func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { 48 return &types.Header{BaseFee: mt.baseFee}, nil 49 } 50 51 func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 52 return []byte{1}, nil 53 } 54 55 func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 56 return 0, nil 57 } 58 59 func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 60 mt.suggestGasPriceCalled = true 61 return mt.gasPrice, nil 62 } 63 64 func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { 65 mt.suggestGasTipCapCalled = true 66 return mt.gasTipCap, nil 67 } 68 69 func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { 70 return 0, nil 71 } 72 73 func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error { 74 return nil 75 } 76 77 type mockCaller struct { 78 codeAtBlockNumber *big.Int 79 callContractBlockNumber *big.Int 80 pendingCodeAtCalled bool 81 pendingCallContractCalled bool 82 } 83 84 func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { 85 mc.codeAtBlockNumber = blockNumber 86 return []byte{1, 2, 3}, nil 87 } 88 89 func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { 90 mc.callContractBlockNumber = blockNumber 91 return nil, nil 92 } 93 94 func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { 95 mc.pendingCodeAtCalled = true 96 return nil, nil 97 } 98 99 func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { 100 mc.pendingCallContractCalled = true 101 return nil, nil 102 } 103 func TestPassingBlockNumber(t *testing.T) { 104 105 mc := &mockCaller{} 106 107 bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ 108 Methods: map[string]abi.Method{ 109 "something": { 110 Name: "something", 111 Outputs: abi.Arguments{}, 112 }, 113 }, 114 }, mc, nil, nil) 115 116 blockNumber := big.NewInt(42) 117 118 bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") 119 120 if mc.callContractBlockNumber != blockNumber { 121 t.Fatalf("CallContract() was not passed the block number") 122 } 123 124 if mc.codeAtBlockNumber != blockNumber { 125 t.Fatalf("CodeAt() was not passed the block number") 126 } 127 128 bc.Call(&bind.CallOpts{}, nil, "something") 129 130 if mc.callContractBlockNumber != nil { 131 t.Fatalf("CallContract() was passed a block number when it should not have been") 132 } 133 134 if mc.codeAtBlockNumber != nil { 135 t.Fatalf("CodeAt() was passed a block number when it should not have been") 136 } 137 138 bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") 139 140 if !mc.pendingCallContractCalled { 141 t.Fatalf("CallContract() was not passed the block number") 142 } 143 144 if !mc.pendingCodeAtCalled { 145 t.Fatalf("CodeAt() was not passed the block number") 146 } 147 } 148 149 const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158" 150 151 func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) { 152 hash := crypto.Keccak256Hash([]byte("testName")) 153 topics := []common.Hash{ 154 crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")), 155 hash, 156 } 157 mockLog := newMockLog(topics, common.HexToHash("0x0")) 158 159 abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` 160 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 161 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 162 163 expectedReceivedMap := map[string]interface{}{ 164 "name": hash, 165 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 166 "amount": big.NewInt(1), 167 "memo": []byte{88}, 168 } 169 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 170 } 171 172 func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { 173 sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) 174 if err != nil { 175 t.Fatal(err) 176 } 177 hash := crypto.Keccak256Hash(sliceBytes) 178 topics := []common.Hash{ 179 crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), 180 hash, 181 } 182 mockLog := newMockLog(topics, common.HexToHash("0x0")) 183 184 abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` 185 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 186 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 187 188 expectedReceivedMap := map[string]interface{}{ 189 "names": hash, 190 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 191 "amount": big.NewInt(1), 192 "memo": []byte{88}, 193 } 194 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 195 } 196 197 func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) { 198 arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")}) 199 if err != nil { 200 t.Fatal(err) 201 } 202 hash := crypto.Keccak256Hash(arrBytes) 203 topics := []common.Hash{ 204 crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), 205 hash, 206 } 207 mockLog := newMockLog(topics, common.HexToHash("0x0")) 208 209 abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` 210 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 211 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 212 213 expectedReceivedMap := map[string]interface{}{ 214 "addresses": hash, 215 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 216 "amount": big.NewInt(1), 217 "memo": []byte{88}, 218 } 219 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 220 } 221 222 func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) { 223 mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2") 224 addrBytes := mockAddress.Bytes() 225 hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) 226 functionSelector := hash[:4] 227 functionTyBytes := append(addrBytes, functionSelector...) 228 var functionTy [24]byte 229 copy(functionTy[:], functionTyBytes[0:24]) 230 topics := []common.Hash{ 231 crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), 232 common.BytesToHash(functionTyBytes), 233 } 234 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 235 abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` 236 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 237 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 238 239 expectedReceivedMap := map[string]interface{}{ 240 "function": functionTy, 241 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 242 "amount": big.NewInt(1), 243 "memo": []byte{88}, 244 } 245 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 246 } 247 248 func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) { 249 bytes := []byte{1, 2, 3, 4, 5} 250 hash := crypto.Keccak256Hash(bytes) 251 topics := []common.Hash{ 252 crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")), 253 hash, 254 } 255 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 256 257 abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` 258 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 259 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 260 261 expectedReceivedMap := map[string]interface{}{ 262 "content": hash, 263 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 264 "amount": big.NewInt(1), 265 "memo": []byte{88}, 266 } 267 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 268 } 269 270 func TestTransactGasFee(t *testing.T) { 271 assert := assert.New(t) 272 273 // GasTipCap and GasFeeCap 274 // When opts.GasTipCap and opts.GasFeeCap are nil 275 mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)} 276 bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 277 opts := &bind.TransactOpts{Signer: mockSign} 278 tx, err := bc.Transact(opts, "") 279 assert.Nil(err) 280 assert.Equal(big.NewInt(5), tx.GasTipCap()) 281 assert.Equal(big.NewInt(205), tx.GasFeeCap()) 282 assert.Nil(opts.GasTipCap) 283 assert.Nil(opts.GasFeeCap) 284 assert.True(mt.suggestGasTipCapCalled) 285 286 // Second call to Transact should use latest suggested GasTipCap 287 mt.gasTipCap = big.NewInt(6) 288 mt.suggestGasTipCapCalled = false 289 tx, err = bc.Transact(opts, "") 290 assert.Nil(err) 291 assert.Equal(big.NewInt(6), tx.GasTipCap()) 292 assert.Equal(big.NewInt(206), tx.GasFeeCap()) 293 assert.True(mt.suggestGasTipCapCalled) 294 295 // GasPrice 296 // When opts.GasPrice is nil 297 mt = &mockTransactor{gasPrice: big.NewInt(5)} 298 bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 299 opts = &bind.TransactOpts{Signer: mockSign} 300 tx, err = bc.Transact(opts, "") 301 assert.Nil(err) 302 assert.Equal(big.NewInt(5), tx.GasPrice()) 303 assert.Nil(opts.GasPrice) 304 assert.True(mt.suggestGasPriceCalled) 305 306 // Second call to Transact should use latest suggested GasPrice 307 mt.gasPrice = big.NewInt(6) 308 mt.suggestGasPriceCalled = false 309 tx, err = bc.Transact(opts, "") 310 assert.Nil(err) 311 assert.Equal(big.NewInt(6), tx.GasPrice()) 312 assert.True(mt.suggestGasPriceCalled) 313 } 314 315 func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { 316 received := make(map[string]interface{}) 317 if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { 318 t.Error(err) 319 } 320 321 if len(received) != len(expected) { 322 t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) 323 } 324 for name, elem := range expected { 325 if !reflect.DeepEqual(elem, received[name]) { 326 t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) 327 } 328 } 329 } 330 331 func newMockLog(topics []common.Hash, txHash common.Hash) types.Log { 332 return types.Log{ 333 Address: common.HexToAddress("0x0"), 334 Topics: topics, 335 Data: hexutil.MustDecode(hexData), 336 BlockNumber: uint64(26), 337 TxHash: txHash, 338 TxIndex: 111, 339 BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}), 340 Index: 7, 341 Removed: false, 342 } 343 }