github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/bind/v2/lib_test.go (about) 1 // Copyright 2024 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 "testing" 23 "time" 24 25 "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" 26 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" 27 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/events" 28 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/nested_libraries" 29 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/solc_errors" 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/eth/ethconfig" 34 "github.com/ethereum/go-ethereum/ethclient" 35 "github.com/ethereum/go-ethereum/ethclient/simulated" 36 "github.com/ethereum/go-ethereum/node" 37 "github.com/ethereum/go-ethereum/params" 38 ) 39 40 var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 41 var testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 42 43 func testSetup() (*backends.SimulatedBackend, error) { 44 backend := simulated.NewBackend( 45 types.GenesisAlloc{ 46 testAddr: {Balance: big.NewInt(10000000000000000)}, 47 }, 48 func(nodeConf *node.Config, ethConf *ethconfig.Config) { 49 ethConf.Genesis.Difficulty = big.NewInt(0) 50 }, 51 ) 52 53 // we should just be able to use the backend directly, instead of using 54 // this deprecated interface. However, the simulated backend no longer 55 // implements backends.SimulatedBackend... 56 bindBackend := backends.SimulatedBackend{ 57 Backend: backend, 58 Client: backend.Client(), 59 } 60 return &bindBackend, nil 61 } 62 63 func makeTestDeployer(backend simulated.Client) func(input, deployer []byte) (common.Address, *types.Transaction, error) { 64 chainId, _ := backend.ChainID(context.Background()) 65 return bind.DefaultDeployer(bind.NewKeyedTransactor(testKey, chainId), backend) 66 } 67 68 // test that deploying a contract with library dependencies works, 69 // verifying by calling method on the deployed contract. 70 func TestDeploymentLibraries(t *testing.T) { 71 bindBackend, err := testSetup() 72 if err != nil { 73 t.Fatalf("err setting up test: %v", err) 74 } 75 defer bindBackend.Backend.Close() 76 77 c := nested_libraries.NewC1() 78 constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) 79 deploymentParams := &bind.DeploymentParams{ 80 Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, 81 Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, 82 } 83 res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend.Client)) 84 if err != nil { 85 t.Fatalf("err: %+v\n", err) 86 } 87 bindBackend.Commit() 88 89 if len(res.Addresses) != 5 { 90 t.Fatalf("deployment should have generated 5 addresses. got %d", len(res.Addresses)) 91 } 92 for _, tx := range res.Txs { 93 _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) 94 if err != nil { 95 t.Fatalf("error deploying library: %+v", err) 96 } 97 } 98 99 doInput := c.PackDo(big.NewInt(1)) 100 contractAddr := res.Addresses[nested_libraries.C1MetaData.ID] 101 callOpts := &bind.CallOpts{From: common.Address{}, Context: context.Background()} 102 instance := c.Instance(bindBackend, contractAddr) 103 internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) 104 if err != nil { 105 t.Fatalf("err unpacking result: %v", err) 106 } 107 if internalCallCount.Uint64() != 6 { 108 t.Fatalf("expected internal call count of 6. got %d.", internalCallCount.Uint64()) 109 } 110 } 111 112 // Same as TestDeployment. However, stagger the deployments with overrides: 113 // first deploy the library deps and then the contract. 114 func TestDeploymentWithOverrides(t *testing.T) { 115 bindBackend, err := testSetup() 116 if err != nil { 117 t.Fatalf("err setting up test: %v", err) 118 } 119 defer bindBackend.Backend.Close() 120 121 // deploy all the library dependencies of our target contract, but not the target contract itself. 122 deploymentParams := &bind.DeploymentParams{ 123 Contracts: nested_libraries.C1MetaData.Deps, 124 } 125 res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) 126 if err != nil { 127 t.Fatalf("err: %+v\n", err) 128 } 129 bindBackend.Commit() 130 131 if len(res.Addresses) != 4 { 132 t.Fatalf("deployment should have generated 4 addresses. got %d", len(res.Addresses)) 133 } 134 for _, tx := range res.Txs { 135 _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) 136 if err != nil { 137 t.Fatalf("error deploying library: %+v", err) 138 } 139 } 140 141 c := nested_libraries.NewC1() 142 constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) 143 overrides := res.Addresses 144 145 // deploy the contract 146 deploymentParams = &bind.DeploymentParams{ 147 Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, 148 Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, 149 Overrides: overrides, 150 } 151 res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) 152 if err != nil { 153 t.Fatalf("err: %+v\n", err) 154 } 155 bindBackend.Commit() 156 157 if len(res.Addresses) != 1 { 158 t.Fatalf("deployment should have generated 1 address. got %d", len(res.Addresses)) 159 } 160 for _, tx := range res.Txs { 161 _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) 162 if err != nil { 163 t.Fatalf("error deploying library: %+v", err) 164 } 165 } 166 167 // call the deployed contract and make sure it returns the correct result 168 doInput := c.PackDo(big.NewInt(1)) 169 instance := c.Instance(bindBackend, res.Addresses[nested_libraries.C1MetaData.ID]) 170 callOpts := new(bind.CallOpts) 171 internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) 172 if err != nil { 173 t.Fatalf("error calling contract: %v", err) 174 } 175 if internalCallCount.Uint64() != 6 { 176 t.Fatalf("expected internal call count of 6. got %d.", internalCallCount.Uint64()) 177 } 178 } 179 180 // returns transaction auth to send a basic transaction from testAddr 181 func defaultTxAuth() *bind.TransactOpts { 182 signer := types.LatestSigner(params.AllDevChainProtocolChanges) 183 opts := &bind.TransactOpts{ 184 From: testAddr, 185 Nonce: nil, 186 Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 187 signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) 188 if err != nil { 189 return nil, err 190 } 191 signedTx, err := tx.WithSignature(signer, signature) 192 if err != nil { 193 return nil, err 194 } 195 return signedTx, nil 196 }, 197 Context: context.Background(), 198 } 199 return opts 200 } 201 202 func TestEvents(t *testing.T) { 203 // test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.) 204 backend, err := testSetup() 205 if err != nil { 206 t.Fatalf("error setting up testing env: %v", err) 207 } 208 deploymentParams := &bind.DeploymentParams{ 209 Contracts: []*bind.MetaData{&events.CMetaData}, 210 } 211 res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend)) 212 if err != nil { 213 t.Fatalf("error deploying contract for testing: %v", err) 214 } 215 216 backend.Commit() 217 if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[events.CMetaData.ID].Hash()); err != nil { 218 t.Fatalf("WaitDeployed failed %v", err) 219 } 220 221 c := events.NewC() 222 instance := c.Instance(backend, res.Addresses[events.CMetaData.ID]) 223 224 newCBasic1Ch := make(chan *events.CBasic1) 225 newCBasic2Ch := make(chan *events.CBasic2) 226 watchOpts := &bind.WatchOpts{} 227 sub1, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic1Event, newCBasic1Ch) 228 if err != nil { 229 t.Fatalf("WatchEvents returned error: %v", err) 230 } 231 sub2, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic2Event, newCBasic2Ch) 232 if err != nil { 233 t.Fatalf("WatchEvents returned error: %v", err) 234 } 235 defer sub1.Unsubscribe() 236 defer sub2.Unsubscribe() 237 238 packedInput := c.PackEmitMulti() 239 tx, err := bind.Transact(instance, defaultTxAuth(), packedInput) 240 if err != nil { 241 t.Fatalf("failed to send transaction: %v", err) 242 } 243 backend.Commit() 244 if _, err := bind.WaitMined(context.Background(), backend, tx.Hash()); err != nil { 245 t.Fatalf("error waiting for tx to be mined: %v", err) 246 } 247 248 timeout := time.NewTimer(2 * time.Second) 249 e1Count := 0 250 e2Count := 0 251 for { 252 select { 253 case <-newCBasic1Ch: 254 e1Count++ 255 case <-newCBasic2Ch: 256 e2Count++ 257 case <-timeout.C: 258 goto done 259 } 260 if e1Count == 2 && e2Count == 1 { 261 break 262 } 263 } 264 done: 265 if e1Count != 2 { 266 t.Fatalf("expected event type 1 count to be 2. got %d", e1Count) 267 } 268 if e2Count != 1 { 269 t.Fatalf("expected event type 2 count to be 1. got %d", e2Count) 270 } 271 272 // now, test that we can filter those same logs after they were included in the chain 273 274 filterOpts := &bind.FilterOpts{ 275 Start: 0, 276 Context: context.Background(), 277 } 278 it, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic1Event) 279 if err != nil { 280 t.Fatalf("error filtering logs %v\n", err) 281 } 282 it2, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic2Event) 283 if err != nil { 284 t.Fatalf("error filtering logs %v\n", err) 285 } 286 287 e1Count = 0 288 e2Count = 0 289 for it.Next() { 290 if err := it.Error(); err != nil { 291 t.Fatalf("got error while iterating events for e1: %v", err) 292 } 293 e1Count++ 294 } 295 for it2.Next() { 296 if err := it2.Error(); err != nil { 297 t.Fatalf("got error while iterating events for e2: %v", err) 298 } 299 e2Count++ 300 } 301 if e1Count != 2 { 302 t.Fatalf("expected e1Count of 2 from filter call. got %d", e1Count) 303 } 304 if e2Count != 1 { 305 t.Fatalf("expected e2Count of 1 from filter call. got %d", e1Count) 306 } 307 } 308 309 func TestErrors(t *testing.T) { 310 // test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.) 311 backend, err := testSetup() 312 if err != nil { 313 t.Fatalf("error setting up testing env: %v", err) 314 } 315 deploymentParams := &bind.DeploymentParams{ 316 Contracts: []*bind.MetaData{&solc_errors.CMetaData}, 317 } 318 res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend)) 319 if err != nil { 320 t.Fatalf("error deploying contract for testing: %v", err) 321 } 322 323 backend.Commit() 324 if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[solc_errors.CMetaData.ID].Hash()); err != nil { 325 t.Fatalf("WaitDeployed failed %v", err) 326 } 327 328 c := solc_errors.NewC() 329 instance := c.Instance(backend, res.Addresses[solc_errors.CMetaData.ID]) 330 packedInput := c.PackFoo() 331 opts := &bind.CallOpts{From: res.Addresses[solc_errors.CMetaData.ID]} 332 _, err = bind.Call[struct{}](instance, opts, packedInput, nil) 333 if err == nil { 334 t.Fatalf("expected call to fail") 335 } 336 raw, hasRevertErrorData := ethclient.RevertErrorData(err) 337 if !hasRevertErrorData { 338 t.Fatalf("expected call error to contain revert error data.") 339 } 340 rawUnpackedErr, err := c.UnpackError(raw) 341 if err != nil { 342 t.Fatalf("expected to unpack error") 343 } 344 345 unpackedErr, ok := rawUnpackedErr.(*solc_errors.CBadThing) 346 if !ok { 347 t.Fatalf("unexpected type for error") 348 } 349 if unpackedErr.Arg1.Cmp(big.NewInt(0)) != 0 { 350 t.Fatalf("bad unpacked error result: expected Arg1 field to be 0. got %s", unpackedErr.Arg1.String()) 351 } 352 if unpackedErr.Arg2.Cmp(big.NewInt(1)) != 0 { 353 t.Fatalf("bad unpacked error result: expected Arg2 field to be 1. got %s", unpackedErr.Arg2.String()) 354 } 355 if unpackedErr.Arg3.Cmp(big.NewInt(2)) != 0 { 356 t.Fatalf("bad unpacked error result: expected Arg3 to be 2. got %s", unpackedErr.Arg3.String()) 357 } 358 if unpackedErr.Arg4 != false { 359 t.Fatalf("bad unpacked error result: expected Arg4 to be false. got true") 360 } 361 }