From ba9015c4306133c15cc6730a058d6767b81403cc Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 9 Dec 2022 14:35:12 -0500 Subject: [PATCH 01/20] feat: adding ability to simulate erc20 transfers and erc721 mint --- cmd/loadtest.go | 195 +++++-- contracts/ERC20.abi | 1 + contracts/ERC20.bin | 1 + contracts/ERC20.go | 802 ++++++++++++++++++++++++++++ contracts/ERC20.sol | 71 +++ contracts/ERC721.abi | 1 + contracts/ERC721.bin | 1 + contracts/ERC721.go | 962 ++++++++++++++++++++++++++++++++++ contracts/ERC721.sol | 197 +++++++ contracts/IERC165.abi | 1 + contracts/IERC165.bin | 0 contracts/IERC20.abi | 1 + contracts/IERC20.bin | 0 contracts/IERC721.abi | 1 + contracts/IERC721.bin | 0 contracts/IERC721Receiver.abi | 1 + contracts/IERC721Receiver.bin | 0 17 files changed, 2200 insertions(+), 35 deletions(-) create mode 100644 contracts/ERC20.abi create mode 100644 contracts/ERC20.bin create mode 100644 contracts/ERC20.go create mode 100644 contracts/ERC20.sol create mode 100644 contracts/ERC721.abi create mode 100644 contracts/ERC721.bin create mode 100644 contracts/ERC721.go create mode 100644 contracts/ERC721.sol create mode 100644 contracts/IERC165.abi create mode 100644 contracts/IERC165.bin create mode 100644 contracts/IERC20.abi create mode 100644 contracts/IERC20.bin create mode 100644 contracts/IERC721.abi create mode 100644 contracts/IERC721.bin create mode 100644 contracts/IERC721Receiver.abi create mode 100644 contracts/IERC721Receiver.bin diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 323b0a6d..864c47a7 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -59,6 +59,8 @@ const ( loadTestModeRandom = "r" loadTestModeStore = "s" loadTestModeLong = "l" + loadTestModeERC20 = "2" + loadTestModeERC721 = "7" codeQualitySeed = "code code code code code code code code code code code quality" codeQualityPrivateKey = "42b6e34dc21598a807dc19d7784c71b2a7a01f6480dc6f58258f78e539f1a1fa" @@ -75,6 +77,8 @@ var ( loadTestModeInc, loadTestModeStore, loadTestModeLong, + loadTestModeERC20, + loadTestModeERC721, // r should be last to exclude it from random mode selection loadTestModeRandom, } @@ -264,7 +268,9 @@ c - call random contract functions f - call specific contract function s - store mode l - long running mode -r - random modes`) +r - random modes +2 - ERC20 Transfers +7 - ERC721 Mints`) ltp.Function = loadtestCmd.PersistentFlags().Uint64P("function", "f", 1, "A specific function to be called if running with `--mode f` ") ltp.Iterations = loadtestCmd.PersistentFlags().Uint64P("iterations", "i", 100, "If we're making contract calls, this controls how many times the contract will execute the instruction in a loop") ltp.ByteCount = loadtestCmd.PersistentFlags().Uint64P("byte-count", "b", 1024, "If we're in store mode, this controls how many bytes we'll try to store in our contract") @@ -522,27 +528,79 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { return err } - // block while the contract is pending - waitCounter := 30 - for { - var ltCounter *big.Int - ltCounter, err = ltContract.GetCallCounter(cops) + err = blockUntilSuccessful(func() error { + _, err = ltContract.GetCallCounter(cops) + return err + }, 30) - if err != nil { - log.Trace().Msg("Waiting for Load Test contract to deploy") - time.Sleep(time.Second) - if waitCounter < 1 { - log.Error().Err(err).Msg("Exhausted waiting period for contract deployment") - return err - } - waitCounter = waitCounter - 1 - continue - } - log.Trace().Interface("counter", ltCounter).Msg("Number of contract calls") - break + if err != nil { + return err } } + var erc20Addr ethcommon.Address + var erc20Contract *contracts.ERC20 + if mode == loadTestModeERC20 || mode == loadTestModeRandom { + erc20Addr, _, _, err = contracts.DeployERC20(tops, c) + if err != nil { + log.Error().Err(err).Msg("Unable to deploy ERC20 contract") + return err + } + log.Trace().Interface("contractaddress", erc20Addr).Msg("ERC20 contract address") + + erc20Contract, err = contracts.NewERC20(erc20Addr, c) + if err != nil { + log.Error().Err(err).Msg("Unable to instantiate new erc20 contract") + return err + } + currentNonce = currentNonce + 1 + + tops.Nonce = new(big.Int).SetUint64(currentNonce) + tops.GasLimit = 10000000 + tops = configureTransactOpts(tops) + + err = blockUntilSuccessful(func() error { + _, err = erc20Contract.Mint(tops, new(big.Int).SetUint64(1_000_000_000_000)) + return err + }, 30) + if err != nil { + return err + } + currentNonce = currentNonce + 1 + } + + var erc721Addr ethcommon.Address + var erc721Contract *contracts.ERC721 + if mode == loadTestModeERC721 || mode == loadTestModeRandom { + erc721Addr, _, _, err = contracts.DeployERC721(tops, c) + if err != nil { + log.Error().Err(err).Msg("Unable to deploy ERC721 contract") + return err + } + log.Trace().Interface("contractaddress", erc721Addr).Msg("ERC721 contract address") + + erc721Contract, err = contracts.NewERC721(erc721Addr, c) + if err != nil { + log.Error().Err(err).Msg("Unable to instantiate new erc20 contract") + return err + } + + currentNonce = currentNonce + 1 + + tops.Nonce = new(big.Int).SetUint64(currentNonce) + tops.GasLimit = 10000000 + tops = configureTransactOpts(tops) + + err = blockUntilSuccessful(func() error { + _, err = erc721Contract.Mint(tops, *ltp.FromETHAddress, new(big.Int).SetUint64(1)) + return err + }, 30) + if err != nil { + return err + } + currentNonce = currentNonce + 1 + } + // deploy and instantiate the delegator contract var delegatorContract *contracts.Delegator if strings.ContainsAny(mode, "rl") || *inputLoadTestParams.ForceContractDeploy { @@ -565,23 +623,13 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { return err } - // block while the contract is pending - waitCounter := 30 - for { - _, err = delegatorContract.Call(tops, ltAddr, []byte{0x12, 0x87, 0xa6, 0x8c}) - if err != nil { - log.Trace().Msg("Waiting for Delegator contract to deploy") - time.Sleep(time.Second) - if waitCounter < 1 { - log.Error().Err(err).Msg("Exhausted waiting period for contract deployment") - return err - } - waitCounter = waitCounter - 1 - continue - } - break + err = blockUntilSuccessful(func() error { + _, err := delegatorContract.Call(tops, ltAddr, []byte{0x12, 0x87, 0xa6, 0x8c}) + return err + }, 30) + if err != nil { + return err } - currentNonce = currentNonce + 1 } @@ -633,6 +681,10 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { startReq, endReq, err = loadtestStore(ctx, c, myNonceValue, ltContract) case loadTestModeLong: startReq, endReq, err = loadtestLong(ctx, c, myNonceValue, delegatorContract, ltAddr) + case loadTestModeERC20: + startReq, endReq, err = loadtestERC20(ctx, c, myNonceValue, erc20Contract, ltAddr) + case loadTestModeERC721: + startReq, endReq, err = loadtestERC721(ctx, c, myNonceValue, erc721Contract, ltAddr) default: log.Error().Str("mode", mode).Msg("We've arrived at a load test mode that we don't recognize") } @@ -652,6 +704,26 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { return nil } +func blockUntilSuccessful(f func() error, tries int) error { + log.Trace().Int("tries", tries).Msg("Starting blocking loop") + waitCounter := tries + for { + err := f() + if err != nil { + if waitCounter < 1 { + log.Error().Err(err).Int("tries", waitCounter).Msg("Exhausted waiting period") + return err + } + log.Trace().Msg("Waiting to deploy") + time.Sleep(time.Second) + waitCounter = waitCounter - 1 + continue + } + break + } + return nil +} + func loadtestTransaction(ctx context.Context, c *ethclient.Client, nonce uint64) (t1 time.Time, t2 time.Time, err error) { ltp := inputLoadTestParams @@ -806,6 +878,60 @@ func loadtestLong(ctx context.Context, c *ethclient.Client, nonce uint64, delega return } +func loadtestERC20(ctx context.Context, c *ethclient.Client, nonce uint64, erc20Contract *contracts.ERC20, ltAddress ethcommon.Address) (t1 time.Time, t2 time.Time, err error) { + ltp := inputLoadTestParams + + to := ltp.ToETHAddress + if *ltp.ToRandom { + to = getRandomAddress() + } + amount := ltp.SendAmount + + chainID := new(big.Int).SetUint64(*ltp.ChainID) + privateKey := ltp.ECDSAPrivateKey + + tops, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID) + if err != nil { + log.Error().Err(err).Msg("Unable create transaction signer") + return + } + tops.Nonce = new(big.Int).SetUint64(nonce) + tops.GasLimit = 10000000 + tops = configureTransactOpts(tops) + + t1 = time.Now() + _, err = erc20Contract.Transfer(tops, *to, amount) + t2 = time.Now() + return +} + +func loadtestERC721(ctx context.Context, c *ethclient.Client, nonce uint64, erc721Contract *contracts.ERC721, ltAddress ethcommon.Address) (t1 time.Time, t2 time.Time, err error) { + ltp := inputLoadTestParams + + to := ltp.ToETHAddress + if *ltp.ToRandom { + to = getRandomAddress() + } + + chainID := new(big.Int).SetUint64(*ltp.ChainID) + privateKey := ltp.ECDSAPrivateKey + + tops, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID) + if err != nil { + log.Error().Err(err).Msg("Unable create transaction signer") + return + } + tops.Nonce = new(big.Int).SetUint64(nonce) + tops.GasLimit = 10000000 + tops = configureTransactOpts(tops) + nftID := new(big.Int).SetUint64(rand.Uint64()) + + t1 = time.Now() + _, err = erc721Contract.Mint(tops, *to, nftID) + t2 = time.Now() + return +} + func recordSample(goRoutineID, requestID int64, err error, start, end time.Time) { s := loadTestSample{} s.GoRoutineID = goRoutineID @@ -882,7 +1008,6 @@ func getRandomAddress() *ethcommon.Address { } realAddr := ethcommon.BytesToAddress(addr) return &realAddr - } func availLoop(ctx context.Context, c *gsrpc.SubstrateAPI) error { diff --git a/contracts/ERC20.abi b/contracts/ERC20.abi new file mode 100644 index 00000000..450ef67c --- /dev/null +++ b/contracts/ERC20.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/ERC20.bin b/contracts/ERC20.bin new file mode 100644 index 00000000..8c02a9d4 --- /dev/null +++ b/contracts/ERC20.bin @@ -0,0 +1 @@ +60806040526040518060400160405280601381526020017f536f6c6964697479206279204578616d706c6500000000000000000000000000815250600390816200004a91906200033c565b506040518060400160405280600781526020017f534f4c4259455800000000000000000000000000000000000000000000000000815250600490816200009191906200033c565b506012600560006101000a81548160ff021916908360ff160217905550348015620000bb57600080fd5b5062000423565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200014457607f821691505b6020821081036200015a5762000159620000fc565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001c47fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000185565b620001d0868362000185565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200021d620002176200021184620001e8565b620001f2565b620001e8565b9050919050565b6000819050919050565b6200023983620001fc565b62000251620002488262000224565b84845462000192565b825550505050565b600090565b6200026862000259565b620002758184846200022e565b505050565b5b818110156200029d57620002916000826200025e565b6001810190506200027b565b5050565b601f821115620002ec57620002b68162000160565b620002c18462000175565b81016020851015620002d1578190505b620002e9620002e08562000175565b8301826200027a565b50505b505050565b600082821c905092915050565b60006200031160001984600802620002f1565b1980831691505092915050565b60006200032c8383620002fe565b9150826002028217905092915050565b6200034782620000c2565b67ffffffffffffffff811115620003635762000362620000cd565b5b6200036f82546200012b565b6200037c828285620002a1565b600060209050601f831160018114620003b457600084156200039f578287015190505b620003ab85826200031e565b8655506200041b565b601f198416620003c48662000160565b60005b82811015620003ee57848901518255600182019150602085019450602081019050620003c7565b868310156200040e57848901516200040a601f891682620002fe565b8355505b6001600288020188555050505b505050505050565b610d6a80620004336000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806342966c681161007157806342966c681461016857806370a082311461018457806395d89b41146101b4578063a0712d68146101d2578063a9059cbb146101ee578063dd62ed3e1461021e576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b661024e565b6040516100c391906109be565b60405180910390f35b6100e660048036038101906100e19190610a79565b6102dc565b6040516100f39190610ad4565b60405180910390f35b6101046103ce565b6040516101119190610afe565b60405180910390f35b610134600480360381019061012f9190610b19565b6103d4565b6040516101419190610ad4565b60405180910390f35b610152610585565b60405161015f9190610b88565b60405180910390f35b610182600480360381019061017d9190610ba3565b610598565b005b61019e60048036038101906101999190610bd0565b61066f565b6040516101ab9190610afe565b60405180910390f35b6101bc610687565b6040516101c991906109be565b60405180910390f35b6101ec60048036038101906101e79190610ba3565b610715565b005b61020860048036038101906102039190610a79565b6107ec565b6040516102159190610ad4565b60405180910390f35b61023860048036038101906102339190610bfd565b610909565b6040516102459190610afe565b60405180910390f35b6003805461025b90610c6c565b80601f016020809104026020016040519081016040528092919081815260200182805461028790610c6c565b80156102d45780601f106102a9576101008083540402835291602001916102d4565b820191906000526020600020905b8154815290600101906020018083116102b757829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516103bc9190610afe565b60405180910390a36001905092915050565b60005481565b600081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546104629190610ccc565b9250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546104b89190610ccc565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461050e9190610d00565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105729190610afe565b60405180910390a3600190509392505050565b600560009054906101000a900460ff1681565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546105e79190610ccc565b92505081905550806000808282546105ff9190610ccc565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516106649190610afe565b60405180910390a350565b60016020528060005260406000206000915090505481565b6004805461069490610c6c565b80601f01602080910402602001604051908101604052809291908181526020018280546106c090610c6c565b801561070d5780601f106106e25761010080835404028352916020019161070d565b820191906000526020600020905b8154815290600101906020018083116106f057829003601f168201915b505050505081565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107649190610d00565b925050819055508060008082825461077c9190610d00565b925050819055503373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107e19190610afe565b60405180910390a350565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461083d9190610ccc565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546108939190610d00565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108f79190610afe565b60405180910390a36001905092915050565b6002602052816000526040600020602052806000526040600020600091509150505481565b600081519050919050565b600082825260208201905092915050565b60005b8381101561096857808201518184015260208101905061094d565b60008484015250505050565b6000601f19601f8301169050919050565b60006109908261092e565b61099a8185610939565b93506109aa81856020860161094a565b6109b381610974565b840191505092915050565b600060208201905081810360008301526109d88184610985565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610a10826109e5565b9050919050565b610a2081610a05565b8114610a2b57600080fd5b50565b600081359050610a3d81610a17565b92915050565b6000819050919050565b610a5681610a43565b8114610a6157600080fd5b50565b600081359050610a7381610a4d565b92915050565b60008060408385031215610a9057610a8f6109e0565b5b6000610a9e85828601610a2e565b9250506020610aaf85828601610a64565b9150509250929050565b60008115159050919050565b610ace81610ab9565b82525050565b6000602082019050610ae96000830184610ac5565b92915050565b610af881610a43565b82525050565b6000602082019050610b136000830184610aef565b92915050565b600080600060608486031215610b3257610b316109e0565b5b6000610b4086828701610a2e565b9350506020610b5186828701610a2e565b9250506040610b6286828701610a64565b9150509250925092565b600060ff82169050919050565b610b8281610b6c565b82525050565b6000602082019050610b9d6000830184610b79565b92915050565b600060208284031215610bb957610bb86109e0565b5b6000610bc784828501610a64565b91505092915050565b600060208284031215610be657610be56109e0565b5b6000610bf484828501610a2e565b91505092915050565b60008060408385031215610c1457610c136109e0565b5b6000610c2285828601610a2e565b9250506020610c3385828601610a2e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610c8457607f821691505b602082108103610c9757610c96610c3d565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610cd782610a43565b9150610ce283610a43565b9250828203905081811115610cfa57610cf9610c9d565b5b92915050565b6000610d0b82610a43565b9150610d1683610a43565b9250828201905080821115610d2e57610d2d610c9d565b5b9291505056fea26469706673582212200fdf5809298c0ed2e73a13e330a564573622aa278dc4a9600c74f8cc0d1d32e764736f6c63430008110033 \ No newline at end of file diff --git a/contracts/ERC20.go b/contracts/ERC20.go new file mode 100644 index 00000000..e099aafd --- /dev/null +++ b/contracts/ERC20.go @@ -0,0 +1,802 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// ERC20MetaData contains all meta data concerning the ERC20 contract. +var ERC20MetaData = &bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040526040518060400160405280601381526020017f536f6c6964697479206279204578616d706c6500000000000000000000000000815250600390816200004a91906200033c565b506040518060400160405280600781526020017f534f4c4259455800000000000000000000000000000000000000000000000000815250600490816200009191906200033c565b506012600560006101000a81548160ff021916908360ff160217905550348015620000bb57600080fd5b5062000423565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200014457607f821691505b6020821081036200015a5762000159620000fc565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620001c47fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000185565b620001d0868362000185565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200021d620002176200021184620001e8565b620001f2565b620001e8565b9050919050565b6000819050919050565b6200023983620001fc565b62000251620002488262000224565b84845462000192565b825550505050565b600090565b6200026862000259565b620002758184846200022e565b505050565b5b818110156200029d57620002916000826200025e565b6001810190506200027b565b5050565b601f821115620002ec57620002b68162000160565b620002c18462000175565b81016020851015620002d1578190505b620002e9620002e08562000175565b8301826200027a565b50505b505050565b600082821c905092915050565b60006200031160001984600802620002f1565b1980831691505092915050565b60006200032c8383620002fe565b9150826002028217905092915050565b6200034782620000c2565b67ffffffffffffffff811115620003635762000362620000cd565b5b6200036f82546200012b565b6200037c828285620002a1565b600060209050601f831160018114620003b457600084156200039f578287015190505b620003ab85826200031e565b8655506200041b565b601f198416620003c48662000160565b60005b82811015620003ee57848901518255600182019150602085019450602081019050620003c7565b868310156200040e57848901516200040a601f891682620002fe565b8355505b6001600288020188555050505b505050505050565b610d6a80620004336000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806342966c681161007157806342966c681461016857806370a082311461018457806395d89b41146101b4578063a0712d68146101d2578063a9059cbb146101ee578063dd62ed3e1461021e576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b661024e565b6040516100c391906109be565b60405180910390f35b6100e660048036038101906100e19190610a79565b6102dc565b6040516100f39190610ad4565b60405180910390f35b6101046103ce565b6040516101119190610afe565b60405180910390f35b610134600480360381019061012f9190610b19565b6103d4565b6040516101419190610ad4565b60405180910390f35b610152610585565b60405161015f9190610b88565b60405180910390f35b610182600480360381019061017d9190610ba3565b610598565b005b61019e60048036038101906101999190610bd0565b61066f565b6040516101ab9190610afe565b60405180910390f35b6101bc610687565b6040516101c991906109be565b60405180910390f35b6101ec60048036038101906101e79190610ba3565b610715565b005b61020860048036038101906102039190610a79565b6107ec565b6040516102159190610ad4565b60405180910390f35b61023860048036038101906102339190610bfd565b610909565b6040516102459190610afe565b60405180910390f35b6003805461025b90610c6c565b80601f016020809104026020016040519081016040528092919081815260200182805461028790610c6c565b80156102d45780601f106102a9576101008083540402835291602001916102d4565b820191906000526020600020905b8154815290600101906020018083116102b757829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516103bc9190610afe565b60405180910390a36001905092915050565b60005481565b600081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546104629190610ccc565b9250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546104b89190610ccc565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461050e9190610d00565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105729190610afe565b60405180910390a3600190509392505050565b600560009054906101000a900460ff1681565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546105e79190610ccc565b92505081905550806000808282546105ff9190610ccc565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516106649190610afe565b60405180910390a350565b60016020528060005260406000206000915090505481565b6004805461069490610c6c565b80601f01602080910402602001604051908101604052809291908181526020018280546106c090610c6c565b801561070d5780601f106106e25761010080835404028352916020019161070d565b820191906000526020600020905b8154815290600101906020018083116106f057829003601f168201915b505050505081565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107649190610d00565b925050819055508060008082825461077c9190610d00565b925050819055503373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107e19190610afe565b60405180910390a350565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461083d9190610ccc565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546108939190610d00565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108f79190610afe565b60405180910390a36001905092915050565b6002602052816000526040600020602052806000526040600020600091509150505481565b600081519050919050565b600082825260208201905092915050565b60005b8381101561096857808201518184015260208101905061094d565b60008484015250505050565b6000601f19601f8301169050919050565b60006109908261092e565b61099a8185610939565b93506109aa81856020860161094a565b6109b381610974565b840191505092915050565b600060208201905081810360008301526109d88184610985565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610a10826109e5565b9050919050565b610a2081610a05565b8114610a2b57600080fd5b50565b600081359050610a3d81610a17565b92915050565b6000819050919050565b610a5681610a43565b8114610a6157600080fd5b50565b600081359050610a7381610a4d565b92915050565b60008060408385031215610a9057610a8f6109e0565b5b6000610a9e85828601610a2e565b9250506020610aaf85828601610a64565b9150509250929050565b60008115159050919050565b610ace81610ab9565b82525050565b6000602082019050610ae96000830184610ac5565b92915050565b610af881610a43565b82525050565b6000602082019050610b136000830184610aef565b92915050565b600080600060608486031215610b3257610b316109e0565b5b6000610b4086828701610a2e565b9350506020610b5186828701610a2e565b9250506040610b6286828701610a64565b9150509250925092565b600060ff82169050919050565b610b8281610b6c565b82525050565b6000602082019050610b9d6000830184610b79565b92915050565b600060208284031215610bb957610bb86109e0565b5b6000610bc784828501610a64565b91505092915050565b600060208284031215610be657610be56109e0565b5b6000610bf484828501610a2e565b91505092915050565b60008060408385031215610c1457610c136109e0565b5b6000610c2285828601610a2e565b9250506020610c3385828601610a2e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610c8457607f821691505b602082108103610c9757610c96610c3d565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610cd782610a43565b9150610ce283610a43565b9250828203905081811115610cfa57610cf9610c9d565b5b92915050565b6000610d0b82610a43565b9150610d1683610a43565b9250828201905080821115610d2e57610d2d610c9d565b5b9291505056fea26469706673582212200fdf5809298c0ed2e73a13e330a564573622aa278dc4a9600c74f8cc0d1d32e764736f6c63430008110033", +} + +// ERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC20MetaData.ABI instead. +var ERC20ABI = ERC20MetaData.ABI + +// ERC20Bin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use ERC20MetaData.Bin instead. +var ERC20Bin = ERC20MetaData.Bin + +// DeployERC20 deploys a new Ethereum contract, binding an instance of ERC20 to it. +func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ERC20, error) { + parsed, err := ERC20MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(ERC20Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil +} + +// ERC20 is an auto generated Go binding around an Ethereum contract. +type ERC20 struct { + ERC20Caller // Read-only binding to the contract + ERC20Transactor // Write-only binding to the contract + ERC20Filterer // Log filterer for contract events +} + +// ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type ERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC20Session struct { + Contract *ERC20 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC20CallerSession struct { + Contract *ERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC20TransactorSession struct { + Contract *ERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type ERC20Raw struct { + Contract *ERC20 // Generic contract binding to access the raw methods on +} + +// ERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC20CallerRaw struct { + Contract *ERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// ERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC20TransactorRaw struct { + Contract *ERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC20 creates a new instance of ERC20, bound to a specific deployed contract. +func NewERC20(address common.Address, backend bind.ContractBackend) (*ERC20, error) { + contract, err := bindERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil +} + +// NewERC20Caller creates a new read-only instance of ERC20, bound to a specific deployed contract. +func NewERC20Caller(address common.Address, caller bind.ContractCaller) (*ERC20Caller, error) { + contract, err := bindERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC20Caller{contract: contract}, nil +} + +// NewERC20Transactor creates a new write-only instance of ERC20, bound to a specific deployed contract. +func NewERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC20Transactor, error) { + contract, err := bindERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC20Transactor{contract: contract}, nil +} + +// NewERC20Filterer creates a new log filterer instance of ERC20, bound to a specific deployed contract. +func NewERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC20Filterer, error) { + contract, err := bindERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC20Filterer{contract: contract}, nil +} + +// bindERC20 binds a generic wrapper to an already deployed contract. +func bindERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC20MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20 *ERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20.Contract.ERC20Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20 *ERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20.Contract.ERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20 *ERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20.Contract.ERC20Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20 *ERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20 *ERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20 *ERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20.Contract.contract.Transact(opts, method, params...) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_ERC20 *ERC20Caller) Allowance(opts *bind.CallOpts, arg0 common.Address, arg1 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "allowance", arg0, arg1) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_ERC20 *ERC20Session) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _ERC20.Contract.Allowance(&_ERC20.CallOpts, arg0, arg1) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_ERC20 *ERC20CallerSession) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _ERC20.Contract.Allowance(&_ERC20.CallOpts, arg0, arg1) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_ERC20 *ERC20Caller) BalanceOf(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "balanceOf", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_ERC20 *ERC20Session) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, arg0) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_ERC20 *ERC20CallerSession) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, arg0) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20Session) Decimals() (uint8, error) { + return _ERC20.Contract.Decimals(&_ERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20CallerSession) Decimals() (uint8, error) { + return _ERC20.Contract.Decimals(&_ERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20Session) Name() (string, error) { + return _ERC20.Contract.Name(&_ERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20CallerSession) Name() (string, error) { + return _ERC20.Contract.Name(&_ERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20Session) Symbol() (string, error) { + return _ERC20.Contract.Symbol(&_ERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20CallerSession) Symbol() (string, error) { + return _ERC20.Contract.Symbol(&_ERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_ERC20 *ERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_ERC20 *ERC20Session) TotalSupply() (*big.Int, error) { + return _ERC20.Contract.TotalSupply(&_ERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_ERC20 *ERC20CallerSession) TotalSupply() (*big.Int, error) { + return _ERC20.Contract.TotalSupply(&_ERC20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_ERC20 *ERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "approve", spender, amount) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_ERC20 *ERC20Session) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Approve(&_ERC20.TransactOpts, spender, amount) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_ERC20 *ERC20TransactorSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Approve(&_ERC20.TransactOpts, spender, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 amount) returns() +func (_ERC20 *ERC20Transactor) Burn(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "burn", amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 amount) returns() +func (_ERC20 *ERC20Session) Burn(amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Burn(&_ERC20.TransactOpts, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 amount) returns() +func (_ERC20 *ERC20TransactorSession) Burn(amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Burn(&_ERC20.TransactOpts, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0xa0712d68. +// +// Solidity: function mint(uint256 amount) returns() +func (_ERC20 *ERC20Transactor) Mint(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "mint", amount) +} + +// Mint is a paid mutator transaction binding the contract method 0xa0712d68. +// +// Solidity: function mint(uint256 amount) returns() +func (_ERC20 *ERC20Session) Mint(amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Mint(&_ERC20.TransactOpts, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0xa0712d68. +// +// Solidity: function mint(uint256 amount) returns() +func (_ERC20 *ERC20TransactorSession) Mint(amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Mint(&_ERC20.TransactOpts, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20Transactor) Transfer(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "transfer", recipient, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20Session) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, recipient, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20TransactorSession) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20Transactor) TransferFrom(opts *bind.TransactOpts, sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "transferFrom", sender, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20Session) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, sender, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_ERC20 *ERC20TransactorSession) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, sender, recipient, amount) +} + +// ERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the ERC20 contract. +type ERC20ApprovalIterator struct { + Event *ERC20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20Approval represents a Approval event raised by the ERC20 contract. +type ERC20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_ERC20 *ERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*ERC20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _ERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &ERC20ApprovalIterator{contract: _ERC20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_ERC20 *ERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *ERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _ERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20Approval) + if err := _ERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_ERC20 *ERC20Filterer) ParseApproval(log types.Log) (*ERC20Approval, error) { + event := new(ERC20Approval) + if err := _ERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the ERC20 contract. +type ERC20TransferIterator struct { + Event *ERC20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20Transfer represents a Transfer event raised by the ERC20 contract. +type ERC20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_ERC20 *ERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ERC20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &ERC20TransferIterator{contract: _ERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_ERC20 *ERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20Transfer) + if err := _ERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_ERC20 *ERC20Filterer) ParseTransfer(log types.Log) (*ERC20Transfer, error) { + event := new(ERC20Transfer) + if err := _ERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/contracts/ERC20.sol b/contracts/ERC20.sol new file mode 100644 index 00000000..b80b8225 --- /dev/null +++ b/contracts/ERC20.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.4; + +// https://solidity-by-example.org/app/erc20/ + +interface IERC20 { + function totalSupply() external view returns (uint); + + function balanceOf(address account) external view returns (uint); + + function transfer(address recipient, uint amount) external returns (bool); + + function allowance(address owner, address spender) external view returns (uint); + + function approve(address spender, uint amount) external returns (bool); + + function transferFrom( + address sender, + address recipient, + uint amount + ) external returns (bool); + + event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint value); +} + +contract ERC20 is IERC20 { + uint public totalSupply; + mapping(address => uint) public balanceOf; + mapping(address => mapping(address => uint)) public allowance; + string public name = "Solidity by Example"; + string public symbol = "SOLBYEX"; + uint8 public decimals = 18; + + function transfer(address recipient, uint amount) external returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(msg.sender, recipient, amount); + return true; + } + + function approve(address spender, uint amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address sender, + address recipient, + uint amount + ) external returns (bool) { + allowance[sender][msg.sender] -= amount; + balanceOf[sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(sender, recipient, amount); + return true; + } + + function mint(uint amount) external { + balanceOf[msg.sender] += amount; + totalSupply += amount; + emit Transfer(address(0), msg.sender, amount); + } + + function burn(uint amount) external { + balanceOf[msg.sender] -= amount; + totalSupply -= amount; + emit Transfer(msg.sender, address(0), amount); + } +} \ No newline at end of file diff --git a/contracts/ERC721.abi b/contracts/ERC721.abi new file mode 100644 index 00000000..823e52d1 --- /dev/null +++ b/contracts/ERC721.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/ERC721.bin b/contracts/ERC721.bin new file mode 100644 index 00000000..a93d695c --- /dev/null +++ b/contracts/ERC721.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50611afb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806342842e0e1161007157806342842e0e146101625780636352211e1461017e57806370a08231146101ae578063a22cb465146101de578063b88d4fde146101fa578063e985e9c514610216576100a9565b806301ffc9a7146100ae578063081812fc146100de578063095ea7b31461010e57806323b872dd1461012a57806340c10f1914610146575b600080fd5b6100c860048036038101906100c39190611185565b610246565b6040516100d591906111cd565b60405180910390f35b6100f860048036038101906100f3919061121e565b610318565b604051610105919061128c565b60405180910390f35b610128600480360381019061012391906112d3565b6103f5565b005b610144600480360381019061013f9190611313565b6105dd565b005b610160600480360381019061015b91906112d3565b6108c7565b005b61017c60048036038101906101779190611313565b6108d5565b005b6101986004803603810190610193919061121e565b610a0d565b6040516101a5919061128c565b60405180910390f35b6101c860048036038101906101c39190611366565b610ab8565b6040516101d591906113a2565b60405180910390f35b6101f860048036038101906101f391906113e9565b610b6f565b005b610214600480360381019061020f919061148e565b610c6c565b005b610230600480360381019061022b9190611516565b610daa565b60405161023d91906111cd565b60405180910390f35b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031157507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b60008073ffffffffffffffffffffffffffffffffffffffff1660008084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036103ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b1906115b3565b60405180910390fd5b6002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600080600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806104ec5750600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b61052b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105229061161f565b60405180910390fd5b826002600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b60008082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461067d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106749061168b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036106ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106e3906116f7565b60405180910390fd5b6106f7833383610dd9565b610736576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072d9061161f565b60405180910390fd5b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081548092919061078690611746565b9190505550600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906107db9061176f565b91905055508160008083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506002600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6108d18282610f0d565b5050565b6108e08383836105dd565b60008273ffffffffffffffffffffffffffffffffffffffff163b14806109c9575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168273ffffffffffffffffffffffffffffffffffffffff1663150b7a023386856040518463ffffffff1660e01b8152600401610965939291906117ee565b6020604051808303816000875af1158015610984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a8919061184d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b610a08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ff906118c6565b60405180910390fd5b505050565b600080600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610ab3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aaa906115b3565b60405180910390fd5b919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b28576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1f90611932565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610c6091906111cd565b60405180910390a35050565b610c778585856105dd565b60008473ffffffffffffffffffffffffffffffffffffffff163b1480610d64575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168473ffffffffffffffffffffffffffffffffffffffff1663150b7a0233888787876040518663ffffffff1660e01b8152600401610d0095949392919061199f565b6020604051808303816000875af1158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d43919061184d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b610da3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d9a906118c6565b60405180910390fd5b5050505050565b60036020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60008373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480610e9b5750600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b80610f0457506002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b90509392505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611a39565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1660008083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461101d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161101490611aa5565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081548092919061106d9061176f565b91905055508160008083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6111628161112d565b811461116d57600080fd5b50565b60008135905061117f81611159565b92915050565b60006020828403121561119b5761119a611123565b5b60006111a984828501611170565b91505092915050565b60008115159050919050565b6111c7816111b2565b82525050565b60006020820190506111e260008301846111be565b92915050565b6000819050919050565b6111fb816111e8565b811461120657600080fd5b50565b600081359050611218816111f2565b92915050565b60006020828403121561123457611233611123565b5b600061124284828501611209565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006112768261124b565b9050919050565b6112868161126b565b82525050565b60006020820190506112a1600083018461127d565b92915050565b6112b08161126b565b81146112bb57600080fd5b50565b6000813590506112cd816112a7565b92915050565b600080604083850312156112ea576112e9611123565b5b60006112f8858286016112be565b925050602061130985828601611209565b9150509250929050565b60008060006060848603121561132c5761132b611123565b5b600061133a868287016112be565b935050602061134b868287016112be565b925050604061135c86828701611209565b9150509250925092565b60006020828403121561137c5761137b611123565b5b600061138a848285016112be565b91505092915050565b61139c816111e8565b82525050565b60006020820190506113b76000830184611393565b92915050565b6113c6816111b2565b81146113d157600080fd5b50565b6000813590506113e3816113bd565b92915050565b60008060408385031215611400576113ff611123565b5b600061140e858286016112be565b925050602061141f858286016113d4565b9150509250929050565b600080fd5b600080fd5b600080fd5b60008083601f84011261144e5761144d611429565b5b8235905067ffffffffffffffff81111561146b5761146a61142e565b5b60208301915083600182028301111561148757611486611433565b5b9250929050565b6000806000806000608086880312156114aa576114a9611123565b5b60006114b8888289016112be565b95505060206114c9888289016112be565b94505060406114da88828901611209565b935050606086013567ffffffffffffffff8111156114fb576114fa611128565b5b61150788828901611438565b92509250509295509295909350565b6000806040838503121561152d5761152c611123565b5b600061153b858286016112be565b925050602061154c858286016112be565b9150509250929050565b600082825260208201905092915050565b7f746f6b656e20646f65736e277420657869737400000000000000000000000000600082015250565b600061159d601383611556565b91506115a882611567565b602082019050919050565b600060208201905081810360008301526115cc81611590565b9050919050565b7f6e6f7420617574686f72697a6564000000000000000000000000000000000000600082015250565b6000611609600e83611556565b9150611614826115d3565b602082019050919050565b60006020820190508181036000830152611638816115fc565b9050919050565b7f66726f6d20213d206f776e657200000000000000000000000000000000000000600082015250565b6000611675600d83611556565b91506116808261163f565b602082019050919050565b600060208201905081810360008301526116a481611668565b9050919050565b7f7472616e7366657220746f207a65726f20616464726573730000000000000000600082015250565b60006116e1601883611556565b91506116ec826116ab565b602082019050919050565b60006020820190508181036000830152611710816116d4565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611751826111e8565b91506000820361176457611763611717565b5b600182039050919050565b600061177a826111e8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036117ac576117ab611717565b5b600182019050919050565b600082825260208201905092915050565b50565b60006117d86000836117b7565b91506117e3826117c8565b600082019050919050565b6000608082019050611803600083018661127d565b611810602083018561127d565b61181d6040830184611393565b818103606083015261182e816117cb565b9050949350505050565b60008151905061184781611159565b92915050565b60006020828403121561186357611862611123565b5b600061187184828501611838565b91505092915050565b7f756e7361666520726563697069656e7400000000000000000000000000000000600082015250565b60006118b0601083611556565b91506118bb8261187a565b602082019050919050565b600060208201905081810360008301526118df816118a3565b9050919050565b7f6f776e6572203d207a65726f2061646472657373000000000000000000000000600082015250565b600061191c601483611556565b9150611927826118e6565b602082019050919050565b6000602082019050818103600083015261194b8161190f565b9050919050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061197e83856117b7565b935061198b838584611952565b61199483611961565b840190509392505050565b60006080820190506119b4600083018861127d565b6119c1602083018761127d565b6119ce6040830186611393565b81810360608301526119e1818486611972565b90509695505050505050565b7f6d696e7420746f207a65726f2061646472657373000000000000000000000000600082015250565b6000611a23601483611556565b9150611a2e826119ed565b602082019050919050565b60006020820190508181036000830152611a5281611a16565b9050919050565b7f616c7265616479206d696e746564000000000000000000000000000000000000600082015250565b6000611a8f600e83611556565b9150611a9a82611a59565b602082019050919050565b60006020820190508181036000830152611abe81611a82565b905091905056fea2646970667358221220d82b488752b3bdd0067d643169008fead66538826c7da2ceeb424297d6bb48b864736f6c63430008110033 \ No newline at end of file diff --git a/contracts/ERC721.go b/contracts/ERC721.go new file mode 100644 index 00000000..1b629065 --- /dev/null +++ b/contracts/ERC721.go @@ -0,0 +1,962 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// ERC721MetaData contains all meta data concerning the ERC721 contract. +var ERC721MetaData = &bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50611afb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806342842e0e1161007157806342842e0e146101625780636352211e1461017e57806370a08231146101ae578063a22cb465146101de578063b88d4fde146101fa578063e985e9c514610216576100a9565b806301ffc9a7146100ae578063081812fc146100de578063095ea7b31461010e57806323b872dd1461012a57806340c10f1914610146575b600080fd5b6100c860048036038101906100c39190611185565b610246565b6040516100d591906111cd565b60405180910390f35b6100f860048036038101906100f3919061121e565b610318565b604051610105919061128c565b60405180910390f35b610128600480360381019061012391906112d3565b6103f5565b005b610144600480360381019061013f9190611313565b6105dd565b005b610160600480360381019061015b91906112d3565b6108c7565b005b61017c60048036038101906101779190611313565b6108d5565b005b6101986004803603810190610193919061121e565b610a0d565b6040516101a5919061128c565b60405180910390f35b6101c860048036038101906101c39190611366565b610ab8565b6040516101d591906113a2565b60405180910390f35b6101f860048036038101906101f391906113e9565b610b6f565b005b610214600480360381019061020f919061148e565b610c6c565b005b610230600480360381019061022b9190611516565b610daa565b60405161023d91906111cd565b60405180910390f35b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061031157507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b60008073ffffffffffffffffffffffffffffffffffffffff1660008084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036103ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b1906115b3565b60405180910390fd5b6002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600080600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806104ec5750600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b61052b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105229061161f565b60405180910390fd5b826002600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b60008082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461067d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106749061168b565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036106ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106e3906116f7565b60405180910390fd5b6106f7833383610dd9565b610736576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072d9061161f565b60405180910390fd5b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081548092919061078690611746565b9190505550600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008154809291906107db9061176f565b91905055508160008083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506002600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6108d18282610f0d565b5050565b6108e08383836105dd565b60008273ffffffffffffffffffffffffffffffffffffffff163b14806109c9575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168273ffffffffffffffffffffffffffffffffffffffff1663150b7a023386856040518463ffffffff1660e01b8152600401610965939291906117ee565b6020604051808303816000875af1158015610984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a8919061184d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b610a08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109ff906118c6565b60405180910390fd5b505050565b600080600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610ab3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aaa906115b3565b60405180910390fd5b919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b28576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1f90611932565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610c6091906111cd565b60405180910390a35050565b610c778585856105dd565b60008473ffffffffffffffffffffffffffffffffffffffff163b1480610d64575063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168473ffffffffffffffffffffffffffffffffffffffff1663150b7a0233888787876040518663ffffffff1660e01b8152600401610d0095949392919061199f565b6020604051808303816000875af1158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d43919061184d565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b610da3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d9a906118c6565b60405180910390fd5b5050505050565b60036020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b60008373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480610e9b5750600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff165b80610f0457506002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b90509392505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611a39565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1660008083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461101d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161101490611aa5565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600081548092919061106d9061176f565b91905055508160008083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6111628161112d565b811461116d57600080fd5b50565b60008135905061117f81611159565b92915050565b60006020828403121561119b5761119a611123565b5b60006111a984828501611170565b91505092915050565b60008115159050919050565b6111c7816111b2565b82525050565b60006020820190506111e260008301846111be565b92915050565b6000819050919050565b6111fb816111e8565b811461120657600080fd5b50565b600081359050611218816111f2565b92915050565b60006020828403121561123457611233611123565b5b600061124284828501611209565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006112768261124b565b9050919050565b6112868161126b565b82525050565b60006020820190506112a1600083018461127d565b92915050565b6112b08161126b565b81146112bb57600080fd5b50565b6000813590506112cd816112a7565b92915050565b600080604083850312156112ea576112e9611123565b5b60006112f8858286016112be565b925050602061130985828601611209565b9150509250929050565b60008060006060848603121561132c5761132b611123565b5b600061133a868287016112be565b935050602061134b868287016112be565b925050604061135c86828701611209565b9150509250925092565b60006020828403121561137c5761137b611123565b5b600061138a848285016112be565b91505092915050565b61139c816111e8565b82525050565b60006020820190506113b76000830184611393565b92915050565b6113c6816111b2565b81146113d157600080fd5b50565b6000813590506113e3816113bd565b92915050565b60008060408385031215611400576113ff611123565b5b600061140e858286016112be565b925050602061141f858286016113d4565b9150509250929050565b600080fd5b600080fd5b600080fd5b60008083601f84011261144e5761144d611429565b5b8235905067ffffffffffffffff81111561146b5761146a61142e565b5b60208301915083600182028301111561148757611486611433565b5b9250929050565b6000806000806000608086880312156114aa576114a9611123565b5b60006114b8888289016112be565b95505060206114c9888289016112be565b94505060406114da88828901611209565b935050606086013567ffffffffffffffff8111156114fb576114fa611128565b5b61150788828901611438565b92509250509295509295909350565b6000806040838503121561152d5761152c611123565b5b600061153b858286016112be565b925050602061154c858286016112be565b9150509250929050565b600082825260208201905092915050565b7f746f6b656e20646f65736e277420657869737400000000000000000000000000600082015250565b600061159d601383611556565b91506115a882611567565b602082019050919050565b600060208201905081810360008301526115cc81611590565b9050919050565b7f6e6f7420617574686f72697a6564000000000000000000000000000000000000600082015250565b6000611609600e83611556565b9150611614826115d3565b602082019050919050565b60006020820190508181036000830152611638816115fc565b9050919050565b7f66726f6d20213d206f776e657200000000000000000000000000000000000000600082015250565b6000611675600d83611556565b91506116808261163f565b602082019050919050565b600060208201905081810360008301526116a481611668565b9050919050565b7f7472616e7366657220746f207a65726f20616464726573730000000000000000600082015250565b60006116e1601883611556565b91506116ec826116ab565b602082019050919050565b60006020820190508181036000830152611710816116d4565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611751826111e8565b91506000820361176457611763611717565b5b600182039050919050565b600061177a826111e8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036117ac576117ab611717565b5b600182019050919050565b600082825260208201905092915050565b50565b60006117d86000836117b7565b91506117e3826117c8565b600082019050919050565b6000608082019050611803600083018661127d565b611810602083018561127d565b61181d6040830184611393565b818103606083015261182e816117cb565b9050949350505050565b60008151905061184781611159565b92915050565b60006020828403121561186357611862611123565b5b600061187184828501611838565b91505092915050565b7f756e7361666520726563697069656e7400000000000000000000000000000000600082015250565b60006118b0601083611556565b91506118bb8261187a565b602082019050919050565b600060208201905081810360008301526118df816118a3565b9050919050565b7f6f776e6572203d207a65726f2061646472657373000000000000000000000000600082015250565b600061191c601483611556565b9150611927826118e6565b602082019050919050565b6000602082019050818103600083015261194b8161190f565b9050919050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061197e83856117b7565b935061198b838584611952565b61199483611961565b840190509392505050565b60006080820190506119b4600083018861127d565b6119c1602083018761127d565b6119ce6040830186611393565b81810360608301526119e1818486611972565b90509695505050505050565b7f6d696e7420746f207a65726f2061646472657373000000000000000000000000600082015250565b6000611a23601483611556565b9150611a2e826119ed565b602082019050919050565b60006020820190508181036000830152611a5281611a16565b9050919050565b7f616c7265616479206d696e746564000000000000000000000000000000000000600082015250565b6000611a8f600e83611556565b9150611a9a82611a59565b602082019050919050565b60006020820190508181036000830152611abe81611a82565b905091905056fea2646970667358221220d82b488752b3bdd0067d643169008fead66538826c7da2ceeb424297d6bb48b864736f6c63430008110033", +} + +// ERC721ABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC721MetaData.ABI instead. +var ERC721ABI = ERC721MetaData.ABI + +// ERC721Bin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use ERC721MetaData.Bin instead. +var ERC721Bin = ERC721MetaData.Bin + +// DeployERC721 deploys a new Ethereum contract, binding an instance of ERC721 to it. +func DeployERC721(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ERC721, error) { + parsed, err := ERC721MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(ERC721Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ERC721{ERC721Caller: ERC721Caller{contract: contract}, ERC721Transactor: ERC721Transactor{contract: contract}, ERC721Filterer: ERC721Filterer{contract: contract}}, nil +} + +// ERC721 is an auto generated Go binding around an Ethereum contract. +type ERC721 struct { + ERC721Caller // Read-only binding to the contract + ERC721Transactor // Write-only binding to the contract + ERC721Filterer // Log filterer for contract events +} + +// ERC721Caller is an auto generated read-only Go binding around an Ethereum contract. +type ERC721Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC721Transactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC721Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC721Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC721Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC721Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC721Session struct { + Contract *ERC721 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC721CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC721CallerSession struct { + Contract *ERC721Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC721TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC721TransactorSession struct { + Contract *ERC721Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC721Raw is an auto generated low-level Go binding around an Ethereum contract. +type ERC721Raw struct { + Contract *ERC721 // Generic contract binding to access the raw methods on +} + +// ERC721CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC721CallerRaw struct { + Contract *ERC721Caller // Generic read-only contract binding to access the raw methods on +} + +// ERC721TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC721TransactorRaw struct { + Contract *ERC721Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC721 creates a new instance of ERC721, bound to a specific deployed contract. +func NewERC721(address common.Address, backend bind.ContractBackend) (*ERC721, error) { + contract, err := bindERC721(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC721{ERC721Caller: ERC721Caller{contract: contract}, ERC721Transactor: ERC721Transactor{contract: contract}, ERC721Filterer: ERC721Filterer{contract: contract}}, nil +} + +// NewERC721Caller creates a new read-only instance of ERC721, bound to a specific deployed contract. +func NewERC721Caller(address common.Address, caller bind.ContractCaller) (*ERC721Caller, error) { + contract, err := bindERC721(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC721Caller{contract: contract}, nil +} + +// NewERC721Transactor creates a new write-only instance of ERC721, bound to a specific deployed contract. +func NewERC721Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC721Transactor, error) { + contract, err := bindERC721(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC721Transactor{contract: contract}, nil +} + +// NewERC721Filterer creates a new log filterer instance of ERC721, bound to a specific deployed contract. +func NewERC721Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC721Filterer, error) { + contract, err := bindERC721(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC721Filterer{contract: contract}, nil +} + +// bindERC721 binds a generic wrapper to an already deployed contract. +func bindERC721(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC721MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC721 *ERC721Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC721.Contract.ERC721Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC721 *ERC721Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC721.Contract.ERC721Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC721 *ERC721Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC721.Contract.ERC721Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC721 *ERC721CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC721.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC721 *ERC721TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC721.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC721 *ERC721TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC721.Contract.contract.Transact(opts, method, params...) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address owner) view returns(uint256) +func (_ERC721 *ERC721Caller) BalanceOf(opts *bind.CallOpts, owner common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "balanceOf", owner) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address owner) view returns(uint256) +func (_ERC721 *ERC721Session) BalanceOf(owner common.Address) (*big.Int, error) { + return _ERC721.Contract.BalanceOf(&_ERC721.CallOpts, owner) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address owner) view returns(uint256) +func (_ERC721 *ERC721CallerSession) BalanceOf(owner common.Address) (*big.Int, error) { + return _ERC721.Contract.BalanceOf(&_ERC721.CallOpts, owner) +} + +// GetApproved is a free data retrieval call binding the contract method 0x081812fc. +// +// Solidity: function getApproved(uint256 id) view returns(address) +func (_ERC721 *ERC721Caller) GetApproved(opts *bind.CallOpts, id *big.Int) (common.Address, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "getApproved", id) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetApproved is a free data retrieval call binding the contract method 0x081812fc. +// +// Solidity: function getApproved(uint256 id) view returns(address) +func (_ERC721 *ERC721Session) GetApproved(id *big.Int) (common.Address, error) { + return _ERC721.Contract.GetApproved(&_ERC721.CallOpts, id) +} + +// GetApproved is a free data retrieval call binding the contract method 0x081812fc. +// +// Solidity: function getApproved(uint256 id) view returns(address) +func (_ERC721 *ERC721CallerSession) GetApproved(id *big.Int) (common.Address, error) { + return _ERC721.Contract.GetApproved(&_ERC721.CallOpts, id) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address , address ) view returns(bool) +func (_ERC721 *ERC721Caller) IsApprovedForAll(opts *bind.CallOpts, arg0 common.Address, arg1 common.Address) (bool, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "isApprovedForAll", arg0, arg1) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address , address ) view returns(bool) +func (_ERC721 *ERC721Session) IsApprovedForAll(arg0 common.Address, arg1 common.Address) (bool, error) { + return _ERC721.Contract.IsApprovedForAll(&_ERC721.CallOpts, arg0, arg1) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address , address ) view returns(bool) +func (_ERC721 *ERC721CallerSession) IsApprovedForAll(arg0 common.Address, arg1 common.Address) (bool, error) { + return _ERC721.Contract.IsApprovedForAll(&_ERC721.CallOpts, arg0, arg1) +} + +// OwnerOf is a free data retrieval call binding the contract method 0x6352211e. +// +// Solidity: function ownerOf(uint256 id) view returns(address owner) +func (_ERC721 *ERC721Caller) OwnerOf(opts *bind.CallOpts, id *big.Int) (common.Address, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "ownerOf", id) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// OwnerOf is a free data retrieval call binding the contract method 0x6352211e. +// +// Solidity: function ownerOf(uint256 id) view returns(address owner) +func (_ERC721 *ERC721Session) OwnerOf(id *big.Int) (common.Address, error) { + return _ERC721.Contract.OwnerOf(&_ERC721.CallOpts, id) +} + +// OwnerOf is a free data retrieval call binding the contract method 0x6352211e. +// +// Solidity: function ownerOf(uint256 id) view returns(address owner) +func (_ERC721 *ERC721CallerSession) OwnerOf(id *big.Int) (common.Address, error) { + return _ERC721.Contract.OwnerOf(&_ERC721.CallOpts, id) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) pure returns(bool) +func (_ERC721 *ERC721Caller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) pure returns(bool) +func (_ERC721 *ERC721Session) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _ERC721.Contract.SupportsInterface(&_ERC721.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) pure returns(bool) +func (_ERC721 *ERC721CallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _ERC721.Contract.SupportsInterface(&_ERC721.CallOpts, interfaceId) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 id) returns() +func (_ERC721 *ERC721Transactor) Approve(opts *bind.TransactOpts, spender common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "approve", spender, id) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 id) returns() +func (_ERC721 *ERC721Session) Approve(spender common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Approve(&_ERC721.TransactOpts, spender, id) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 id) returns() +func (_ERC721 *ERC721TransactorSession) Approve(spender common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Approve(&_ERC721.TransactOpts, spender, id) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 id) returns() +func (_ERC721 *ERC721Transactor) Mint(opts *bind.TransactOpts, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "mint", to, id) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 id) returns() +func (_ERC721 *ERC721Session) Mint(to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Mint(&_ERC721.TransactOpts, to, id) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 id) returns() +func (_ERC721 *ERC721TransactorSession) Mint(to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Mint(&_ERC721.TransactOpts, to, id) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721Transactor) SafeTransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "safeTransferFrom", from, to, id) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721Session) SafeTransferFrom(from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom(&_ERC721.TransactOpts, from, to, id) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721TransactorSession) SafeTransferFrom(from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom(&_ERC721.TransactOpts, from, to, id) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, bytes data) returns() +func (_ERC721 *ERC721Transactor) SafeTransferFrom0(opts *bind.TransactOpts, from common.Address, to common.Address, id *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "safeTransferFrom0", from, to, id, data) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, bytes data) returns() +func (_ERC721 *ERC721Session) SafeTransferFrom0(from common.Address, to common.Address, id *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom0(&_ERC721.TransactOpts, from, to, id, data) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, bytes data) returns() +func (_ERC721 *ERC721TransactorSession) SafeTransferFrom0(from common.Address, to common.Address, id *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom0(&_ERC721.TransactOpts, from, to, id, data) +} + +// SetApprovalForAll is a paid mutator transaction binding the contract method 0xa22cb465. +// +// Solidity: function setApprovalForAll(address operator, bool approved) returns() +func (_ERC721 *ERC721Transactor) SetApprovalForAll(opts *bind.TransactOpts, operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "setApprovalForAll", operator, approved) +} + +// SetApprovalForAll is a paid mutator transaction binding the contract method 0xa22cb465. +// +// Solidity: function setApprovalForAll(address operator, bool approved) returns() +func (_ERC721 *ERC721Session) SetApprovalForAll(operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC721.Contract.SetApprovalForAll(&_ERC721.TransactOpts, operator, approved) +} + +// SetApprovalForAll is a paid mutator transaction binding the contract method 0xa22cb465. +// +// Solidity: function setApprovalForAll(address operator, bool approved) returns() +func (_ERC721 *ERC721TransactorSession) SetApprovalForAll(operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC721.Contract.SetApprovalForAll(&_ERC721.TransactOpts, operator, approved) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "transferFrom", from, to, id) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721Session) TransferFrom(from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.TransferFrom(&_ERC721.TransactOpts, from, to, id) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 id) returns() +func (_ERC721 *ERC721TransactorSession) TransferFrom(from common.Address, to common.Address, id *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.TransferFrom(&_ERC721.TransactOpts, from, to, id) +} + +// ERC721ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the ERC721 contract. +type ERC721ApprovalIterator struct { + Event *ERC721Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC721ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC721Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC721Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC721ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC721ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC721Approval represents a Approval event raised by the ERC721 contract. +type ERC721Approval struct { + Owner common.Address + Spender common.Address + Id *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 indexed id) +func (_ERC721 *ERC721Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address, id []*big.Int) (*ERC721ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC721.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule, idRule) + if err != nil { + return nil, err + } + return &ERC721ApprovalIterator{contract: _ERC721.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 indexed id) +func (_ERC721 *ERC721Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *ERC721Approval, owner []common.Address, spender []common.Address, id []*big.Int) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC721.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule, idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC721Approval) + if err := _ERC721.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 indexed id) +func (_ERC721 *ERC721Filterer) ParseApproval(log types.Log) (*ERC721Approval, error) { + event := new(ERC721Approval) + if err := _ERC721.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC721ApprovalForAllIterator is returned from FilterApprovalForAll and is used to iterate over the raw logs and unpacked data for ApprovalForAll events raised by the ERC721 contract. +type ERC721ApprovalForAllIterator struct { + Event *ERC721ApprovalForAll // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC721ApprovalForAllIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC721ApprovalForAll) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC721ApprovalForAll) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC721ApprovalForAllIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC721ApprovalForAllIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC721ApprovalForAll represents a ApprovalForAll event raised by the ERC721 contract. +type ERC721ApprovalForAll struct { + Owner common.Address + Operator common.Address + Approved bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApprovalForAll is a free log retrieval operation binding the contract event 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31. +// +// Solidity: event ApprovalForAll(address indexed owner, address indexed operator, bool approved) +func (_ERC721 *ERC721Filterer) FilterApprovalForAll(opts *bind.FilterOpts, owner []common.Address, operator []common.Address) (*ERC721ApprovalForAllIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + + logs, sub, err := _ERC721.contract.FilterLogs(opts, "ApprovalForAll", ownerRule, operatorRule) + if err != nil { + return nil, err + } + return &ERC721ApprovalForAllIterator{contract: _ERC721.contract, event: "ApprovalForAll", logs: logs, sub: sub}, nil +} + +// WatchApprovalForAll is a free log subscription operation binding the contract event 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31. +// +// Solidity: event ApprovalForAll(address indexed owner, address indexed operator, bool approved) +func (_ERC721 *ERC721Filterer) WatchApprovalForAll(opts *bind.WatchOpts, sink chan<- *ERC721ApprovalForAll, owner []common.Address, operator []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + + logs, sub, err := _ERC721.contract.WatchLogs(opts, "ApprovalForAll", ownerRule, operatorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC721ApprovalForAll) + if err := _ERC721.contract.UnpackLog(event, "ApprovalForAll", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApprovalForAll is a log parse operation binding the contract event 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31. +// +// Solidity: event ApprovalForAll(address indexed owner, address indexed operator, bool approved) +func (_ERC721 *ERC721Filterer) ParseApprovalForAll(log types.Log) (*ERC721ApprovalForAll, error) { + event := new(ERC721ApprovalForAll) + if err := _ERC721.contract.UnpackLog(event, "ApprovalForAll", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC721TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the ERC721 contract. +type ERC721TransferIterator struct { + Event *ERC721Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC721TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC721Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC721Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC721TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC721TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC721Transfer represents a Transfer event raised by the ERC721 contract. +type ERC721Transfer struct { + From common.Address + To common.Address + Id *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 indexed id) +func (_ERC721 *ERC721Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address, id []*big.Int) (*ERC721TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC721.contract.FilterLogs(opts, "Transfer", fromRule, toRule, idRule) + if err != nil { + return nil, err + } + return &ERC721TransferIterator{contract: _ERC721.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 indexed id) +func (_ERC721 *ERC721Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ERC721Transfer, from []common.Address, to []common.Address, id []*big.Int) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC721.contract.WatchLogs(opts, "Transfer", fromRule, toRule, idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC721Transfer) + if err := _ERC721.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 indexed id) +func (_ERC721 *ERC721Filterer) ParseTransfer(log types.Log) (*ERC721Transfer, error) { + event := new(ERC721Transfer) + if err := _ERC721.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/contracts/ERC721.sol b/contracts/ERC721.sol new file mode 100644 index 00000000..4e65dc96 --- /dev/null +++ b/contracts/ERC721.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.4; + +interface IERC165 { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +interface IERC721 is IERC165 { + function balanceOf(address owner) external view returns (uint balance); + + function ownerOf(uint tokenId) external view returns (address owner); + + function safeTransferFrom( + address from, + address to, + uint tokenId + ) external; + + function safeTransferFrom( + address from, + address to, + uint tokenId, + bytes calldata data + ) external; + + function transferFrom( + address from, + address to, + uint tokenId + ) external; + + function approve(address to, uint tokenId) external; + + function getApproved(uint tokenId) external view returns (address operator); + + function setApprovalForAll(address operator, bool _approved) external; + + function isApprovedForAll(address owner, address operator) + external + view + returns (bool); +} + +interface IERC721Receiver { + function onERC721Received( + address operator, + address from, + uint tokenId, + bytes calldata data + ) external returns (bytes4); +} + +contract ERC721 is IERC721 { + event Transfer(address indexed from, address indexed to, uint indexed id); + event Approval(address indexed owner, address indexed spender, uint indexed id); + event ApprovalForAll( + address indexed owner, + address indexed operator, + bool approved + ); + + // Mapping from token ID to owner address + mapping(uint => address) internal _ownerOf; + + // Mapping owner address to token count + mapping(address => uint) internal _balanceOf; + + // Mapping from token ID to approved address + mapping(uint => address) internal _approvals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) public isApprovedForAll; + + function supportsInterface(bytes4 interfaceId) external pure returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC165).interfaceId; + } + + function ownerOf(uint id) external view returns (address owner) { + owner = _ownerOf[id]; + require(owner != address(0), "token doesn't exist"); + } + + function balanceOf(address owner) external view returns (uint) { + require(owner != address(0), "owner = zero address"); + return _balanceOf[owner]; + } + + function setApprovalForAll(address operator, bool approved) external { + isApprovedForAll[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + function approve(address spender, uint id) external { + address owner = _ownerOf[id]; + require( + msg.sender == owner || isApprovedForAll[owner][msg.sender], + "not authorized" + ); + + _approvals[id] = spender; + + emit Approval(owner, spender, id); + } + + function getApproved(uint id) external view returns (address) { + require(_ownerOf[id] != address(0), "token doesn't exist"); + return _approvals[id]; + } + + function _isApprovedOrOwner( + address owner, + address spender, + uint id + ) internal view returns (bool) { + return (spender == owner || + isApprovedForAll[owner][spender] || + spender == _approvals[id]); + } + + function transferFrom( + address from, + address to, + uint id + ) public { + require(from == _ownerOf[id], "from != owner"); + require(to != address(0), "transfer to zero address"); + + require(_isApprovedOrOwner(from, msg.sender, id), "not authorized"); + + _balanceOf[from]--; + _balanceOf[to]++; + _ownerOf[id] = to; + + delete _approvals[id]; + + emit Transfer(from, to, id); + } + + function safeTransferFrom( + address from, + address to, + uint id + ) external { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + IERC721Receiver(to).onERC721Received(msg.sender, from, id, "") == + IERC721Receiver.onERC721Received.selector, + "unsafe recipient" + ); + } + + function safeTransferFrom( + address from, + address to, + uint id, + bytes calldata data + ) external { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + IERC721Receiver(to).onERC721Received(msg.sender, from, id, data) == + IERC721Receiver.onERC721Received.selector, + "unsafe recipient" + ); + } + + function _mint(address to, uint id) internal { + require(to != address(0), "mint to zero address"); + require(_ownerOf[id] == address(0), "already minted"); + + _balanceOf[to]++; + _ownerOf[id] = to; + + emit Transfer(address(0), to, id); + } + + function mint(address to, uint id) external { + _mint(to, id); + } + + function _burn(uint id) internal { + address owner = _ownerOf[id]; + require(owner != address(0), "not minted"); + + _balanceOf[owner] -= 1; + + delete _ownerOf[id]; + delete _approvals[id]; + + emit Transfer(owner, address(0), id); + } +} \ No newline at end of file diff --git a/contracts/IERC165.abi b/contracts/IERC165.abi new file mode 100644 index 00000000..dcdbc15e --- /dev/null +++ b/contracts/IERC165.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/contracts/IERC165.bin b/contracts/IERC165.bin new file mode 100644 index 00000000..e69de29b diff --git a/contracts/IERC20.abi b/contracts/IERC20.abi new file mode 100644 index 00000000..38876a99 --- /dev/null +++ b/contracts/IERC20.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/IERC20.bin b/contracts/IERC20.bin new file mode 100644 index 00000000..e69de29b diff --git a/contracts/IERC721.abi b/contracts/IERC721.abi new file mode 100644 index 00000000..119f8faa --- /dev/null +++ b/contracts/IERC721.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/IERC721.bin b/contracts/IERC721.bin new file mode 100644 index 00000000..e69de29b diff --git a/contracts/IERC721Receiver.abi b/contracts/IERC721Receiver.abi new file mode 100644 index 00000000..ab551930 --- /dev/null +++ b/contracts/IERC721Receiver.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/IERC721Receiver.bin b/contracts/IERC721Receiver.bin new file mode 100644 index 00000000..e69de29b From 7c8155f2f3077255e47d0bbfb6f81937b7991e76 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 9 Dec 2022 17:05:33 -0500 Subject: [PATCH 02/20] fix: the datatype of the tx receipt is wrong --- rpctypes/rpctypes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpctypes/rpctypes.go b/rpctypes/rpctypes.go index 6ca55f90..eaee64ea 100644 --- a/rpctypes/rpctypes.go +++ b/rpctypes/rpctypes.go @@ -160,7 +160,7 @@ type ( ContractAddress RawData20Response `json:"contractAddress"` // logs: Array - Array of log objects, which this transaction generated. - Logs []RawDataResponse `json:"logs"` + Logs []any `json:"logs"` // logsBloom: DATA, 256 Bytes - Bloom filter for light clients to quickly retrieve related logs. It also returns either : LogsBloom RawData256Response `json:"logsBloom"` From fd4e6fc5da5effe56052ad1d27e938684b635cbb Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Fri, 9 Dec 2022 17:05:52 -0500 Subject: [PATCH 03/20] feat: starting to add some summary data --- cmd/dumpblocks.go | 26 ++++----- cmd/loadtest.go | 134 +++++++++++++++++++++++++++++++--------------- 2 files changed, 103 insertions(+), 57 deletions(-) diff --git a/cmd/dumpblocks.go b/cmd/dumpblocks.go index 6e4ddc9d..1dc974d7 100644 --- a/cmd/dumpblocks.go +++ b/cmd/dumpblocks.go @@ -33,8 +33,8 @@ import ( type ( dumpblocksArgs struct { URL string - Start int64 - End int64 + Start uint64 + End uint64 BatchSize uint64 Threads *uint } @@ -67,7 +67,7 @@ var dumpblocksCmd = &cobra.Command{ for start < end { rangeStart := start - rangeEnd := rangeStart + int64(inputDumpblocks.BatchSize) + rangeEnd := rangeStart + inputDumpblocks.BatchSize if rangeEnd > end { rangeEnd = end @@ -75,16 +75,16 @@ var dumpblocksCmd = &cobra.Command{ pool <- true wg.Add(1) - log.Info().Int64("start", rangeStart).Int64("end", rangeEnd).Msg("getting range") + log.Info().Uint64("start", rangeStart).Uint64("end", rangeEnd).Msg("getting range") go func() { defer wg.Done() for { failCount := 0 - blocks, err := getBlockRange(ctx, rangeStart, rangeEnd, ec, inputDumpblocks.URL) + blocks, err := getBlockRange(ctx, rangeStart, rangeEnd, ec) if err != nil { failCount = failCount + 1 if failCount > 5 { - log.Error().Int64("rangeStart", rangeStart).Int64("rangeEnd", rangeEnd).Msg("unable to fetch blocks") + log.Error().Uint64("rangeStart", rangeStart).Uint64("rangeEnd", rangeEnd).Msg("unable to fetch blocks") break } time.Sleep(5 * time.Second) @@ -92,11 +92,11 @@ var dumpblocksCmd = &cobra.Command{ } failCount = 0 - receipts, err := getReceipts(ctx, blocks, ec, inputDumpblocks.URL) + receipts, err := getReceipts(ctx, blocks, ec) if err != nil { failCount = failCount + 1 if failCount > 5 { - log.Error().Int64("rangeStart", rangeStart).Int64("rangeEnd", rangeEnd).Msg("unable to fetch receipts") + log.Error().Uint64("rangeStart", rangeStart).Uint64("rangeEnd", rangeEnd).Msg("unable to fetch receipts") break } time.Sleep(5 * time.Second) @@ -149,8 +149,8 @@ var dumpblocksCmd = &cobra.Command{ start, end = end, start } inputDumpblocks.URL = args[0] - inputDumpblocks.Start = start - inputDumpblocks.End = end + inputDumpblocks.Start = uint64(start) + inputDumpblocks.End = uint64(end) // realistically, this probably shoudln't be bigger than 999. Most Providers seem to cap at 1000 inputDumpblocks.BatchSize = 150 @@ -165,14 +165,14 @@ func init() { } -func getBlockRange(ctx context.Context, from, to int64, c *ethrpc.Client, url string) ([]*json.RawMessage, error) { +func getBlockRange(ctx context.Context, from, to uint64, c *ethrpc.Client) ([]*json.RawMessage, error) { blms := make([]ethrpc.BatchElem, 0) for i := from; i <= to; i = i + 1 { r := new(json.RawMessage) var err error blms = append(blms, ethrpc.BatchElem{ Method: "eth_getBlockByNumber", - Args: []interface{}{"0x" + strconv.FormatInt(i, 16), true}, + Args: []interface{}{"0x" + strconv.FormatUint(i, 16), true}, Result: r, Error: err, }) @@ -203,7 +203,7 @@ type ( } ) -func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Client, url string) ([]*json.RawMessage, error) { +func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Client) ([]*json.RawMessage, error) { txHashes := make([]string, 0) for _, rb := range rawBlocks { var sb simpleRPCBlock diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 864c47a7..4e68380a 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -20,7 +20,9 @@ import ( "context" "crypto/ecdsa" "encoding/hex" + "encoding/json" "fmt" + "github.com/maticnetwork/polygon-cli/rpctypes" "io" "math/big" "math/rand" @@ -82,6 +84,7 @@ var ( // r should be last to exclude it from random mode selection loadTestModeRandom, } + hexwords = []byte{ 0x00, 0x0F, 0xF1, 0xCE, 0x00, 0xBA, 0xB1, 0x0C, @@ -191,6 +194,10 @@ func setLogLevel(ltp loadTestParams) { } type ( + blockSummary struct { + Block *rpctypes.RawBlockResponse + Reciepts []rpctypes.RawTxReceipt + } hexwordReader struct { } loadTestSample struct { @@ -411,7 +418,7 @@ func runLoadTest(ctx context.Context) error { return err } - return mainLoop(ctx, ec) + return mainLoop(ctx, ec, rpc) } } @@ -421,7 +428,6 @@ func runLoadTest(ctx context.Context) error { loadTestResults = make([]loadTestSample, 0) errCh := make(chan error) go func() { - // errCh <- mainLoop(ctx, ec) errCh <- loopFunc() }() @@ -480,7 +486,7 @@ func printResults(lts []loadTestSample) { fmt.Printf("Num errors: %d\n", numErrors) } -func mainLoop(ctx context.Context, c *ethclient.Client) error { +func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) error { ltp := inputLoadTestParams log.Trace().Interface("Input Params", ltp).Msg("Params") @@ -635,7 +641,13 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { var currentNonceMutex sync.Mutex var i int64 - + startBlockNumber, err := c.BlockNumber(ctx) + if err != nil { + log.Error().Err(err).Msg("Failed to get current block number") + return err + } + startNonce := currentNonce + log.Debug().Uint64("currenNonce", currentNonce).Msg("Starting main loadtest loop") var wg sync.WaitGroup for i = 0; i < routines; i = i + 1 { log.Trace().Int64("routine", i).Msg("Starting Thread") @@ -701,7 +713,8 @@ func mainLoop(ctx context.Context, c *ethclient.Client) error { } log.Trace().Msg("Finished starting go routines. Waiting..") wg.Wait() - return nil + log.Debug().Uint64("currenNonce", currentNonce).Msg("Finished main loadtest loop") + return summarizeTransactions(ctx, c, rpc, startBlockNumber, startNonce, currentNonce) } func blockUntilSuccessful(f func() error, tries int) error { @@ -944,45 +957,6 @@ func recordSample(goRoutineID, requestID int64, err error, start, end time.Time) loadTestResults = append(loadTestResults, s) } -/* -// This function is unused -func createLoadTesterContract(ctx context.Context, c *ethclient.Client, nonce uint64, gasPrice *big.Int) (*ethtypes.Receipt, error) { - var gasLimit uint64 = 0x192f64 - contract, err := contracts.GetLoadTesterBytes() - if err != nil { - return nil, err - } - - ltp := inputLoadTestParams - chainID := new(big.Int).SetUint64(*ltp.ChainID) - privateKey := ltp.ECDSAPrivateKey - - tx := ethtypes.NewContractCreation(nonce, big.NewInt(0), gasLimit, gasPrice, contract) - stx, err := ethtypes.SignTx(tx, ethtypes.NewEIP155Signer(chainID), privateKey) - if err != nil { - log.Error().Err(err).Msg("Unable to sign transaction") - return nil, err - } - - err = c.SendTransaction(ctx, stx) - if err != nil { - return nil, err - } - - wait := time.Millisecond * 500 - for i := 0; i < 5; i = i + 1 { - receipt, err := c.TransactionReceipt(ctx, stx.Hash()) - if err == nil { - return receipt, nil - } - time.Sleep(wait) - wait = time.Duration(float64(wait) * 1.5) - } - - return nil, fmt.Errorf("unable to get tx receipt") -} -*/ - func hexwordRead(b []byte) (int, error) { hw := hexwordReader{} return io.ReadFull(&hw, b) @@ -1280,3 +1254,75 @@ func configureTransactOpts(tops *bind.TransactOpts) *bind.TransactOpts { } return tops } +func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client, startBlockNumber, startNonce, endNonce uint64) error { + ltp := inputLoadTestParams + var err error + var lastBlockNumber uint64 + for { + lastBlockNumber, err = c.BlockNumber(ctx) + if err != nil { + return err + } + + currentNonce, err := c.NonceAt(ctx, *ltp.FromETHAddress, new(big.Int).SetUint64(lastBlockNumber)) + if err != nil { + return err + } + if currentNonce < endNonce { + log.Trace().Uint64("endNonce", endNonce).Uint64("currentNonce", currentNonce).Msg("Not all transactions have been mined. Waiting") + time.Sleep(5 * time.Second) + continue + } + break + } + + rawBlocks, err := getBlockRange(ctx, startBlockNumber, lastBlockNumber, rpc) + if err != nil { + return err + } + // FIXME: This code shouldn't really work like this. getBlockRange and getReceipts need to be move to a package + inputDumpblocks.BatchSize = 150 + rawTxReceipts, err := getReceipts(ctx, rawBlocks, rpc) + if err != nil { + return err + } + + blocks := make([]rpctypes.RawBlockResponse, 0) + for _, b := range rawBlocks { + var block rpctypes.RawBlockResponse + err := json.Unmarshal(*b, &block) + if err != nil { + log.Error().Err(err).Msg("error decoding block response") + return err + } + blocks = append(blocks, block) + } + log.Info().Int("len", len(blocks)).Msg("block summary") + + txReceipts := make([]rpctypes.RawTxReceipt, 0) + for _, r := range rawTxReceipts { + if isEmptyJSONResponse(r) { + continue + } + var receipt rpctypes.RawTxReceipt + err := json.Unmarshal(*r, &receipt) + if err != nil { + log.Error().Err(err).Msg("error decoding tx receipt response") + return err + } + txReceipts = append(txReceipts, receipt) + } + log.Info().Int("len", len(txReceipts)).Msg("receipt summary") + + // blockData := make(map[uint64]blockSummary) + return nil + +} + +func isEmptyJSONResponse(r *json.RawMessage) bool { + rawJson := []byte(*r) + if len(rawJson) == 0 { + return true + } + return false +} From c78f208133863ed79d2c4a8785a45cb2801e64d6 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 10:00:27 -0500 Subject: [PATCH 04/20] feat: implementing basic summary --- cmd/loadtest.go | 133 ++++++++++++++++++++++++++++++++++++++++++- rpctypes/rpctypes.go | 7 +-- 2 files changed, 132 insertions(+), 8 deletions(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 4e68380a..88db2ca6 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -23,13 +23,19 @@ import ( "encoding/json" "fmt" "github.com/maticnetwork/polygon-cli/rpctypes" + "golang.org/x/exp/constraints" + "golang.org/x/text/language" + "golang.org/x/text/message" + "golang.org/x/text/number" "io" + "math" "math/big" "math/rand" "net/url" "os" "os/signal" "regexp" + "sort" "strings" "sync" "time" @@ -196,7 +202,7 @@ func setLogLevel(ltp loadTestParams) { type ( blockSummary struct { Block *rpctypes.RawBlockResponse - Reciepts []rpctypes.RawTxReceipt + Receipts map[ethcommon.Hash]rpctypes.RawTxReceipt } hexwordReader struct { } @@ -1314,7 +1320,22 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc } log.Info().Int("len", len(txReceipts)).Msg("receipt summary") - // blockData := make(map[uint64]blockSummary) + blockData := make(map[uint64]blockSummary, 0) + for k, b := range blocks { + bs := blockSummary{} + bs.Block = &blocks[k] + bs.Receipts = make(map[ethcommon.Hash]rpctypes.RawTxReceipt, 0) + blockData[b.Number.ToUint64()] = bs + } + + for _, r := range txReceipts { + bn := r.BlockNumber.ToUint64() + bs := blockData[bn] + bs.Receipts[r.TransactionHash.ToHash()] = r + blockData[bn] = bs + } + + printBlockSummary(blockData, startNonce, endNonce) return nil } @@ -1326,3 +1347,111 @@ func isEmptyJSONResponse(r *json.RawMessage) bool { } return false } + +func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) { + filterBlockSummary(bs, startNonce, endNonce) + mapKeys := getSortedMapKeys(bs) + if len(mapKeys) == 0 { + return + } + + fmt.Println("Block level summary of load test") + var totalTransactions uint64 = 0 + var totalGasUsed uint64 = 0 + p := message.NewPrinter(language.English) + + for _, v := range mapKeys { + summary := bs[v] + gasUsed := getTotalGasUsed(summary.Receipts) + blockUtilization := float64(gasUsed) / summary.Block.GasLimit.ToFloat64() + if gasUsed == 0 { + blockUtilization = 0 + } + _, _ = p.Printf("Block number: %v\tTime: %s\tGas Limit: %v\tGas Used: %v\tNum Tx: %v\tUtilization %v\n", + number.Decimal(summary.Block.Number.ToUint64()), + time.Unix(summary.Block.Timestamp.ToInt64(), 0), + number.Decimal(summary.Block.GasLimit.ToUint64()), + number.Decimal(gasUsed), + number.Decimal(len(summary.Block.Transactions)), + number.Percent(blockUtilization)) + totalTransactions += uint64(len(summary.Block.Transactions)) + totalGasUsed += gasUsed + } + firstBlock := bs[mapKeys[0]].Block + lastBlock := bs[mapKeys[len(mapKeys)-1]].Block + totalMiningTime := time.Duration(lastBlock.Timestamp.ToInt64()-firstBlock.Timestamp.ToInt64()) * time.Second + tps := float64(totalTransactions) / totalMiningTime.Seconds() + gaspersec := float64(totalGasUsed) / totalMiningTime.Seconds() + p.Printf("Total Mining Time: %s\n", totalMiningTime) + p.Printf("Total Transactions: %v\n", number.Decimal(totalTransactions)) + p.Printf("Total Gas Used: %v\n", number.Decimal(totalGasUsed)) + p.Printf("Transactions per sec: %v\n", number.Decimal(tps)) + p.Printf("Gas Per Second: %v\n", number.Decimal(gaspersec)) +} +func getTotalGasUsed(receipts map[ethcommon.Hash]rpctypes.RawTxReceipt) uint64 { + var totalGasUsed uint64 = 0 + for _, receipt := range receipts { + totalGasUsed += receipt.GasUsed.ToUint64() + } + return totalGasUsed +} + +func filterBlockSummary(blockSummaries map[uint64]blockSummary, startNonce, endNonce uint64) { + validTx := make(map[ethcommon.Hash]struct{}, 0) + var minBlock uint64 = math.MaxUint64 + var maxBlock uint64 = 0 + for _, bs := range blockSummaries { + for _, tx := range bs.Block.Transactions { + if tx.Nonce.ToUint64() >= startNonce && tx.Nonce.ToUint64() <= endNonce { + validTx[tx.Hash.ToHash()] = struct{}{} + if tx.BlockNumber.ToUint64() < minBlock { + minBlock = tx.BlockNumber.ToUint64() + } + if tx.BlockNumber.ToUint64() > maxBlock { + maxBlock = tx.BlockNumber.ToUint64() + } + } + } + } + keys := getSortedMapKeys(blockSummaries) + for _, k := range keys { + if k < minBlock { + delete(blockSummaries, k) + } + if k > maxBlock { + delete(blockSummaries, k) + } + } + + for _, bs := range blockSummaries { + filteredTransactions := make([]rpctypes.RawTransactionResponse, 0) + for txKey, tx := range bs.Block.Transactions { + if _, hasKey := validTx[tx.Hash.ToHash()]; hasKey { + filteredTransactions = append(filteredTransactions, bs.Block.Transactions[txKey]) + } + } + bs.Block.Transactions = filteredTransactions + filteredReceipts := make(map[ethcommon.Hash]rpctypes.RawTxReceipt, 0) + for receiptKey, receipt := range bs.Receipts { + if _, hasKey := validTx[receipt.TransactionHash.ToHash()]; hasKey { + filteredReceipts[receipt.TransactionHash.ToHash()] = bs.Receipts[receiptKey] + } + } + bs.Receipts = filteredReceipts + + } +} + +func getSortedMapKeys[V any, K constraints.Ordered](m map[K]V) []K { + keys := make([]K, 0) + for k := range m { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + if keys[i] < keys[j] { + return true + } + return false + }) + return keys +} diff --git a/rpctypes/rpctypes.go b/rpctypes/rpctypes.go index eaee64ea..79b57545 100644 --- a/rpctypes/rpctypes.go +++ b/rpctypes/rpctypes.go @@ -436,12 +436,7 @@ func (r RawQuantityResponse) ToUint64() uint64 { return uint64(result) } func (r RawQuantityResponse) ToFloat64() float64 { - hexString := normalizeHexString(string(r)) - result, err := strconv.ParseFloat(hexString, 64) - if err != nil { - return 0 - } - return result + return float64(r.ToInt64()) } func (r RawQuantityResponse) ToInt64() int64 { From 7b7b2e3057fce093c1afbba89bef0cf8a3e7fb28 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 10:05:50 -0500 Subject: [PATCH 05/20] ci: adding an update --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2bf29b5..a3f8ff23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,7 @@ jobs: - name: perform cross build and compress binaries shell: bash run: | + sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu pwd From 558ff6d788c9857858df920170a77aa736bf70ea Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 10:12:33 -0500 Subject: [PATCH 06/20] fix(ci): shadow needs to be installed in ci process --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3f8ff23..c80e764d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,8 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu + go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest + pwd make cross pushd out From 2348d084f64a883c135dc81bed144c9fcbc8a319 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 10:21:48 -0500 Subject: [PATCH 07/20] fix: lints/shadows --- cmd/loadtest.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 88db2ca6..feb446c1 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -636,7 +636,7 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro } err = blockUntilSuccessful(func() error { - _, err := delegatorContract.Call(tops, ltAddr, []byte{0x12, 0x87, 0xa6, 0x8c}) + _, err = delegatorContract.Call(tops, ltAddr, []byte{0x12, 0x87, 0xa6, 0x8c}) return err }, 30) if err != nil { @@ -1264,13 +1264,14 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc ltp := inputLoadTestParams var err error var lastBlockNumber uint64 + var currentNonce uint64 for { lastBlockNumber, err = c.BlockNumber(ctx) if err != nil { return err } - currentNonce, err := c.NonceAt(ctx, *ltp.FromETHAddress, new(big.Int).SetUint64(lastBlockNumber)) + currentNonce, err = c.NonceAt(ctx, *ltp.FromETHAddress, new(big.Int).SetUint64(lastBlockNumber)) if err != nil { return err } @@ -1342,10 +1343,7 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc func isEmptyJSONResponse(r *json.RawMessage) bool { rawJson := []byte(*r) - if len(rawJson) == 0 { - return true - } - return false + return len(rawJson) == 0 } func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) { @@ -1448,10 +1446,7 @@ func getSortedMapKeys[V any, K constraints.Ordered](m map[K]V) []K { keys = append(keys, k) } sort.Slice(keys, func(i, j int) bool { - if keys[i] < keys[j] { - return true - } - return false + return keys[i] < keys[j] }) return keys } From cebef41f58741e7addc9b2e6274b803ccc240cf8 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 10:26:53 -0500 Subject: [PATCH 08/20] fix: adding another ci tool --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c80e764d..376f548e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,7 @@ jobs: sudo apt-get install -y gcc-aarch64-linux-gnu go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1 pwd make cross From 53cc5bf0ba14c17d4e31495ad12caf4e6cc84df7 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 11:08:50 -0500 Subject: [PATCH 09/20] fix: adding some controls for thread count --- cmd/dumpblocks.go | 3 +++ cmd/loadtest.go | 67 +++++++++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/cmd/dumpblocks.go b/cmd/dumpblocks.go index 1dc974d7..d581b9ed 100644 --- a/cmd/dumpblocks.go +++ b/cmd/dumpblocks.go @@ -177,6 +177,8 @@ func getBlockRange(ctx context.Context, from, to uint64, c *ethrpc.Client) ([]*j Error: err, }) } + log.Trace().Uint64("start", from).Uint64("end", to).Msg("Fetching block range") + err := c.BatchCallContext(ctx, blms) if err != nil { log.Error().Err(err).Msg("rpc issue fetching blocks") @@ -242,6 +244,7 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl end = uint64(len(blms) - 1) } + log.Trace().Uint64("start", start).Uint64("end", end).Msg("Fetching tx receipt range") // json: cannot unmarshal object into Go value of type []rpc.jsonrpcMessage // The error occurs when we call batchcallcontext with a single transaction for some reason. // polycli dumpblocks -c 1 http://127.0.0.1:9209/ 34457958 34458108 diff --git a/cmd/loadtest.go b/cmd/loadtest.go index feb446c1..8f1de4aa 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -35,6 +35,7 @@ import ( "os" "os/signal" "regexp" + "runtime" "sort" "strings" "sync" @@ -216,30 +217,31 @@ type ( } loadTestParams struct { // inputs - Requests *int64 - Concurrency *int64 - TimeLimit *int64 - Verbosity *int64 - PrettyLogs *bool - ToRandom *bool - URL *url.URL - ChainID *uint64 - PrivateKey *string - ToAddress *string - HexSendAmount *string - RateLimit *float64 - Mode *string - Function *uint64 - Iterations *uint64 - ByteCount *uint64 - Seed *int64 - IsAvail *bool - AvailAppID *uint32 - LtAddress *string - DelAddress *string - ForceContractDeploy *bool - ForceGasLimit *uint64 - ForceGasPrice *uint64 + Requests *int64 + Concurrency *int64 + TimeLimit *int64 + Verbosity *int64 + PrettyLogs *bool + ToRandom *bool + URL *url.URL + ChainID *uint64 + PrivateKey *string + ToAddress *string + HexSendAmount *string + RateLimit *float64 + Mode *string + Function *uint64 + Iterations *uint64 + ByteCount *uint64 + Seed *int64 + IsAvail *bool + AvailAppID *uint32 + LtAddress *string + DelAddress *string + ForceContractDeploy *bool + ForceGasLimit *uint64 + ForceGasPrice *uint64 + ShouldProduceSummary *bool // Computed CurrentGas *big.Int @@ -295,6 +297,7 @@ r - random modes ltp.ForceContractDeploy = loadtestCmd.PersistentFlags().Bool("force-contract-deploy", false, "Some loadtest modes don't require a contract deployment. Set this flag to true to force contract deployments. This will still respect the --del-address and --il-address flags.") ltp.ForceGasLimit = loadtestCmd.PersistentFlags().Uint64("gas-limit", 0, "In environments where the gas limit can't be computed on the fly, we can specify it manually") ltp.ForceGasPrice = loadtestCmd.PersistentFlags().Uint64("gas-price", 0, "In environments where the gas price can't be estimated, we can specify it manually") + ltp.ShouldProduceSummary = loadtestCmd.PersistentFlags().Bool("summarize", false, "Should we produce an execution summary after the load test has finished. If you're running a large loadtest, this can take a long time") inputLoadTestParams = *ltp // TODO batch size @@ -461,6 +464,7 @@ func runLoadTest(ctx context.Context) error { } else if ptc > 0 { log.Info().Uint("pending", ptc).Msg("there are still oustanding transactions. There might be issues restarting with the same sending key until those transactions clear") } + log.Info().Msg("Finished") return nil } @@ -720,7 +724,13 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro log.Trace().Msg("Finished starting go routines. Waiting..") wg.Wait() log.Debug().Uint64("currenNonce", currentNonce).Msg("Finished main loadtest loop") - return summarizeTransactions(ctx, c, rpc, startBlockNumber, startNonce, currentNonce) + if *ltp.ShouldProduceSummary { + err = summarizeTransactions(ctx, c, rpc, startBlockNumber, startNonce, currentNonce) + if err != nil { + log.Error().Err(err).Msg("There was an issue creating the load test summary") + } + } + return nil } func blockUntilSuccessful(f func() error, tries int) error { @@ -1283,12 +1293,17 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc break } + log.Trace().Uint64("currentNonce", currentNonce).Uint64("startblock", startBlockNumber).Uint64("endblock", lastBlockNumber).Msg("It looks like all transactions have been mined") + log.Trace().Msg("Starting block range capture") rawBlocks, err := getBlockRange(ctx, startBlockNumber, lastBlockNumber, rpc) if err != nil { return err } - // FIXME: This code shouldn't really work like this. getBlockRange and getReceipts need to be move to a package + // FIXME: This code shouldn't really work like this. getBlockRange and getReceipts need to be moved to a package inputDumpblocks.BatchSize = 150 + cpuCount := uint(runtime.NumCPU()) + inputDumpblocks.Threads = &cpuCount + log.Trace().Msg("Starting tx receipt capture") rawTxReceipts, err := getReceipts(ctx, rawBlocks, rpc) if err != nil { return err From a595a6cf6ce69a7da0b76335929f8cb7be630cf4 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 11:44:15 -0500 Subject: [PATCH 10/20] feat: adding goroutines for txreceipt fetching --- cmd/loadtest.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 8f1de4aa..8150b2e9 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -1300,12 +1300,32 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc return err } // FIXME: This code shouldn't really work like this. getBlockRange and getReceipts need to be moved to a package - inputDumpblocks.BatchSize = 150 + inputDumpblocks.BatchSize = 999 cpuCount := uint(runtime.NumCPU()) - inputDumpblocks.Threads = &cpuCount + var txGroup sync.WaitGroup + threadPool := make(chan bool, cpuCount) log.Trace().Msg("Starting tx receipt capture") - rawTxReceipts, err := getReceipts(ctx, rawBlocks, rpc) - if err != nil { + rawTxReceipts := make([]*json.RawMessage, 0) + var rawTxReceiptsLock sync.Mutex + var txGroupErr error + for k := range rawBlocks { + threadPool <- true + txGroup.Add(1) + go func(b *json.RawMessage) { + receipt, err := getReceipts(ctx, []*json.RawMessage{b}, rpc) + if err != nil { + txGroupErr = err + } + rawTxReceiptsLock.Lock() + rawTxReceipts = append(rawTxReceipts, receipt...) + rawTxReceiptsLock.Unlock() + <-threadPool + txGroup.Done() + }(rawBlocks[k]) + } + txGroup.Wait() + if txGroupErr != nil { + log.Error().Err(err).Msg("one of the threads fetching tx receipts failed") return err } From d75a1f4e111957ba73779ff132b028909d7d3475 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 11:55:58 -0500 Subject: [PATCH 11/20] fix: shadow --- cmd/loadtest.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 8150b2e9..675d2410 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -1312,7 +1312,8 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc threadPool <- true txGroup.Add(1) go func(b *json.RawMessage) { - receipt, err := getReceipts(ctx, []*json.RawMessage{b}, rpc) + var receipt []*json.RawMessage + receipt, err = getReceipts(ctx, []*json.RawMessage{b}, rpc) if err != nil { txGroupErr = err } From d576a480b24285c2c4a88a335bcf6efb7df7e255 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sat, 10 Dec 2022 12:19:52 -0500 Subject: [PATCH 12/20] docs: adding some notes --- cmd/loadtest.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 675d2410..1268c237 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -1300,6 +1300,7 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc return err } // FIXME: This code shouldn't really work like this. getBlockRange and getReceipts need to be moved to a package + // TODO: Add some kind of decimation to avoid summarizing for 10 minutes? inputDumpblocks.BatchSize = 999 cpuCount := uint(runtime.NumCPU()) var txGroup sync.WaitGroup @@ -1421,6 +1422,7 @@ func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) p.Printf("Total Gas Used: %v\n", number.Decimal(totalGasUsed)) p.Printf("Transactions per sec: %v\n", number.Decimal(tps)) p.Printf("Gas Per Second: %v\n", number.Decimal(gaspersec)) + // TODO: Add some kind of indicatin of block time variance } func getTotalGasUsed(receipts map[ethcommon.Hash]rpctypes.RawTxReceipt) uint64 { var totalGasUsed uint64 = 0 From 172758f422d6900e258dce4d844f2a23c1de4395 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sun, 11 Dec 2022 10:52:20 -0500 Subject: [PATCH 13/20] feat: adding block latency --- cmd/dumpblocks.go | 9 ++++-- cmd/loadtest.go | 76 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/cmd/dumpblocks.go b/cmd/dumpblocks.go index d581b9ed..1aae2b24 100644 --- a/cmd/dumpblocks.go +++ b/cmd/dumpblocks.go @@ -201,12 +201,14 @@ type ( Hash string `json:"hash"` } simpleRPCBlock struct { + Number string `json:"number"` Transactions []simpleRPCTransaction `json:"transactions"` } ) func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Client) ([]*json.RawMessage, error) { txHashes := make([]string, 0) + txHashMap := make(map[string]string, 0) for _, rb := range rawBlocks { var sb simpleRPCBlock err := json.Unmarshal(*rb, &sb) @@ -216,6 +218,7 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl } for _, tx := range sb.Transactions { txHashes = append(txHashes, tx.Hash) + txHashMap[tx.Hash] = sb.Number } } @@ -224,7 +227,8 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl } blms := make([]ethrpc.BatchElem, 0) - for _, tx := range txHashes { + blmsBlockMap := make(map[int]string, 0) + for i, tx := range txHashes { r := new(json.RawMessage) var err error blms = append(blms, ethrpc.BatchElem{ @@ -233,6 +237,7 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl Result: r, Error: err, }) + blmsBlockMap[i] = txHashMap[tx] } var start uint64 = 0 @@ -244,7 +249,7 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl end = uint64(len(blms) - 1) } - log.Trace().Uint64("start", start).Uint64("end", end).Msg("Fetching tx receipt range") + log.Trace().Str("startblock", blmsBlockMap[int(start)]).Uint64("start", start).Uint64("end", end).Msg("Fetching tx receipt range") // json: cannot unmarshal object into Go value of type []rpc.jsonrpcMessage // The error occurs when we call batchcallcontext with a single transaction for some reason. // polycli dumpblocks -c 1 http://127.0.0.1:9209/ 34457958 34458108 diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 1268c237..9f8c1fd7 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -202,8 +202,9 @@ func setLogLevel(ltp loadTestParams) { type ( blockSummary struct { - Block *rpctypes.RawBlockResponse - Receipts map[ethcommon.Hash]rpctypes.RawTxReceipt + Block *rpctypes.RawBlockResponse + Receipts map[ethcommon.Hash]rpctypes.RawTxReceipt + Latencies map[uint64]time.Duration } hexwordReader struct { } @@ -214,6 +215,7 @@ type ( WaitTime time.Duration Receipt string IsError bool + Nonce uint64 } loadTestParams struct { // inputs @@ -710,7 +712,7 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro default: log.Error().Str("mode", mode).Msg("We've arrived at a load test mode that we don't recognize") } - recordSample(i, j, err, startReq, endReq) + recordSample(i, j, err, startReq, endReq, myNonceValue) if err != nil { log.Trace().Err(err).Msg("Recorded an error while sending transactions") } @@ -961,12 +963,13 @@ func loadtestERC721(ctx context.Context, c *ethclient.Client, nonce uint64, erc7 return } -func recordSample(goRoutineID, requestID int64, err error, start, end time.Time) { +func recordSample(goRoutineID, requestID int64, err error, start, end time.Time, nonce uint64) { s := loadTestSample{} s.GoRoutineID = goRoutineID s.RequestID = requestID s.RequestTime = start s.WaitTime = end.Sub(start) + s.Nonce = nonce if err != nil { s.IsError = true } @@ -1105,7 +1108,7 @@ func availLoop(ctx context.Context, c *gsrpc.SubstrateAPI) error { default: log.Error().Str("mode", mode).Msg("We've arrived at a load test mode that we don't recognize") } - recordSample(i, j, err, startReq, endReq) + recordSample(i, j, err, startReq, endReq, myNonceValue) if err != nil { log.Trace().Err(err).Msg("Recorded an error while sending transactions") } @@ -1363,6 +1366,7 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc bs := blockSummary{} bs.Block = &blocks[k] bs.Receipts = make(map[ethcommon.Hash]rpctypes.RawTxReceipt, 0) + bs.Latencies = make(map[uint64]time.Duration, 0) blockData[b.Number.ToUint64()] = bs } @@ -1373,6 +1377,21 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc blockData[bn] = bs } + nonceTimes := make(map[uint64]time.Time, 0) + for _, ltr := range loadTestResults { + nonceTimes[ltr.Nonce] = ltr.RequestTime + } + + for _, bs := range blockData { + for _, tx := range bs.Block.Transactions { + // TODO: What happens when the system clock of the load tester isn't in sync with the system clock of the miner? + mineTime := time.Unix(bs.Block.Timestamp.ToInt64(), 0) + requestTime := nonceTimes[tx.Nonce.ToUint64()] + txLatency := mineTime.Sub(requestTime) + bs.Latencies[tx.Nonce.ToUint64()] = txLatency + } + } + printBlockSummary(blockData, startNonce, endNonce) return nil @@ -1395,20 +1414,27 @@ func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) var totalGasUsed uint64 = 0 p := message.NewPrinter(language.English) + allLatencies := make([]time.Duration, 0) for _, v := range mapKeys { summary := bs[v] gasUsed := getTotalGasUsed(summary.Receipts) + blockLatencies := getMapValues(summary.Latencies) + minLatency, medianLatency, maxLatency := getMinMedianMax(blockLatencies) + allLatencies = append(allLatencies, blockLatencies...) blockUtilization := float64(gasUsed) / summary.Block.GasLimit.ToFloat64() if gasUsed == 0 { blockUtilization = 0 } - _, _ = p.Printf("Block number: %v\tTime: %s\tGas Limit: %v\tGas Used: %v\tNum Tx: %v\tUtilization %v\n", + _, _ = p.Printf("Block number: %v\tTime: %s\tGas Limit: %v\tGas Used: %v\tNum Tx: %v\tUtilization %v\tLatencies: %v\t%v\t%v\n", number.Decimal(summary.Block.Number.ToUint64()), time.Unix(summary.Block.Timestamp.ToInt64(), 0), number.Decimal(summary.Block.GasLimit.ToUint64()), number.Decimal(gasUsed), number.Decimal(len(summary.Block.Transactions)), - number.Percent(blockUtilization)) + number.Percent(blockUtilization), + number.Decimal(minLatency.Seconds()), + number.Decimal(medianLatency.Seconds()), + number.Decimal(maxLatency.Seconds())) totalTransactions += uint64(len(summary.Block.Transactions)) totalGasUsed += gasUsed } @@ -1417,11 +1443,14 @@ func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) totalMiningTime := time.Duration(lastBlock.Timestamp.ToInt64()-firstBlock.Timestamp.ToInt64()) * time.Second tps := float64(totalTransactions) / totalMiningTime.Seconds() gaspersec := float64(totalGasUsed) / totalMiningTime.Seconds() + minLatency, medianLatency, maxLatency := getMinMedianMax(allLatencies) + p.Printf("Total Mining Time: %s\n", totalMiningTime) p.Printf("Total Transactions: %v\n", number.Decimal(totalTransactions)) p.Printf("Total Gas Used: %v\n", number.Decimal(totalGasUsed)) p.Printf("Transactions per sec: %v\n", number.Decimal(tps)) p.Printf("Gas Per Second: %v\n", number.Decimal(gaspersec)) + p.Printf("Latencies - Min: %v\tMedian: %v\tMax: %v\n", number.Decimal(minLatency.Seconds()), number.Decimal(medianLatency.Seconds()), number.Decimal(maxLatency.Seconds())) // TODO: Add some kind of indicatin of block time variance } func getTotalGasUsed(receipts map[ethcommon.Hash]rpctypes.RawTxReceipt) uint64 { @@ -1431,6 +1460,39 @@ func getTotalGasUsed(receipts map[ethcommon.Hash]rpctypes.RawTxReceipt) uint64 { } return totalGasUsed } +func getMapValues[K constraints.Ordered, V any](m map[K]V) []V { + newSlice := make([]V, 0) + for _, val := range m { + newSlice = append(newSlice, val) + } + return newSlice +} +func getMinMedianMax[V constraints.Float | constraints.Integer](values []V) (V, V, V) { + sort.Slice(values, func(i, j int) bool { + return values[i] < values[j] + }) + half := len(values) / 2 + median := values[half] + if len(values)%2 == 0 { + median = (median + values[half-1]) / V(2) + } + var min V + var max V + for k, v := range values { + if k == 0 { + min = v + max = v + continue + } + if v < min { + min = v + } + if v > max { + max = v + } + } + return min, median, max +} func filterBlockSummary(blockSummaries map[uint64]blockSummary, startNonce, endNonce uint64) { validTx := make(map[ethcommon.Hash]struct{}, 0) From 9c1cb14ebdde591f5312fbec3997cd878e149263 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sun, 11 Dec 2022 12:13:30 -0500 Subject: [PATCH 14/20] fix: bug in tx receipt dumping --- cmd/dumpblocks.go | 4 ++-- cmd/loadtest.go | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cmd/dumpblocks.go b/cmd/dumpblocks.go index 1aae2b24..579da80e 100644 --- a/cmd/dumpblocks.go +++ b/cmd/dumpblocks.go @@ -244,9 +244,9 @@ func getReceipts(ctx context.Context, rawBlocks []*json.RawMessage, c *ethrpc.Cl for { last := false end := start + inputDumpblocks.BatchSize - if int(end) >= len(blms) { + if int(end) > len(blms) { last = true - end = uint64(len(blms) - 1) + end = uint64(len(blms)) } log.Trace().Str("startblock", blmsBlockMap[int(start)]).Uint64("start", start).Uint64("end", end).Msg("Fetching tx receipt range") diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 9f8c1fd7..6e361bf6 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -1347,6 +1347,7 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc log.Info().Int("len", len(blocks)).Msg("block summary") txReceipts := make([]rpctypes.RawTxReceipt, 0) + log.Trace().Int("len", len(rawTxReceipts)).Msg("raw receipts") for _, r := range rawTxReceipts { if isEmptyJSONResponse(r) { continue @@ -1385,7 +1386,8 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc for _, bs := range blockData { for _, tx := range bs.Block.Transactions { // TODO: What happens when the system clock of the load tester isn't in sync with the system clock of the miner? - mineTime := time.Unix(bs.Block.Timestamp.ToInt64(), 0) + // TODO: the timestamp in the chain only has granularity down to the second. How to deal with this + mineTime := time.Unix(bs.Block.Timestamp.ToInt64()+1, 0) requestTime := nonceTimes[tx.Nonce.ToUint64()] txLatency := mineTime.Sub(requestTime) bs.Latencies[tx.Nonce.ToUint64()] = txLatency @@ -1444,14 +1446,29 @@ func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) tps := float64(totalTransactions) / totalMiningTime.Seconds() gaspersec := float64(totalGasUsed) / totalMiningTime.Seconds() minLatency, medianLatency, maxLatency := getMinMedianMax(allLatencies) + sucessfulTx, totalTx := getSuccessfulTransactionCount(bs) + p.Printf("Successful Tx: %v\tTotal Tx: %v\n", number.Decimal(sucessfulTx), number.Decimal(totalTx)) p.Printf("Total Mining Time: %s\n", totalMiningTime) p.Printf("Total Transactions: %v\n", number.Decimal(totalTransactions)) p.Printf("Total Gas Used: %v\n", number.Decimal(totalGasUsed)) p.Printf("Transactions per sec: %v\n", number.Decimal(tps)) p.Printf("Gas Per Second: %v\n", number.Decimal(gaspersec)) p.Printf("Latencies - Min: %v\tMedian: %v\tMax: %v\n", number.Decimal(minLatency.Seconds()), number.Decimal(medianLatency.Seconds()), number.Decimal(maxLatency.Seconds())) - // TODO: Add some kind of indicatin of block time variance + // TODO: Add some kind of indication of block time variance +} +func getSuccessfulTransactionCount(bs map[uint64]blockSummary) (successful, total int64) { + total = 0 + successful = 0 + for _, block := range bs { + for _, receipt := range block.Receipts { + total += 1 + if receipt.Status.ToInt64() == 1 { + successful += 1 + } + } + } + return } func getTotalGasUsed(receipts map[ethcommon.Hash]rpctypes.RawTxReceipt) uint64 { var totalGasUsed uint64 = 0 @@ -1468,6 +1485,9 @@ func getMapValues[K constraints.Ordered, V any](m map[K]V) []V { return newSlice } func getMinMedianMax[V constraints.Float | constraints.Integer](values []V) (V, V, V) { + if len(values) == 0 { + return 0, 0, 0 + } sort.Slice(values, func(i, j int) bool { return values[i] < values[j] }) From 97b8c853564847a54e338a9de970333e73309e60 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sun, 11 Dec 2022 13:10:51 -0500 Subject: [PATCH 15/20] fix: addressing conncurrent slice access issue --- cmd/loadtest.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 6e361bf6..9a92dbe7 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -78,6 +78,7 @@ const ( var ( inputLoadTestParams loadTestParams loadTestResults []loadTestSample + loadTestResutsMutex sync.RWMutex validLoadTestModes = []string{ loadTestModeTransaction, loadTestModeDeploy, @@ -973,7 +974,9 @@ func recordSample(goRoutineID, requestID int64, err error, start, end time.Time, if err != nil { s.IsError = true } + loadTestResutsMutex.Lock() loadTestResults = append(loadTestResults, s) + loadTestResutsMutex.Unlock() } func hexwordRead(b []byte) (int, error) { @@ -1387,9 +1390,16 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc for _, tx := range bs.Block.Transactions { // TODO: What happens when the system clock of the load tester isn't in sync with the system clock of the miner? // TODO: the timestamp in the chain only has granularity down to the second. How to deal with this + // the + 1 here is to deal with pretty obvious error margins due to the way block timestamp works. Not sure + // what the best way to account for this is. In the future we might want to determine what the min reasonable + // latency is... E.g. it's not possible to have a latency lower than the latency between our servers. Then we + // can find the min observed latency and shift all of the other latencies accordingly mineTime := time.Unix(bs.Block.Timestamp.ToInt64()+1, 0) requestTime := nonceTimes[tx.Nonce.ToUint64()] txLatency := mineTime.Sub(requestTime) + if txLatency.Hours() > 2 { + log.Debug().Float64("txHours", txLatency.Hours()).Uint64("nonce", tx.Nonce.ToUint64()).Uint64("blockNumber", bs.Block.Number.ToUint64()).Time("mineTime", mineTime).Time("requestTime", requestTime).Msg("Encountered transaction with more than 2 hours latency") + } bs.Latencies[tx.Nonce.ToUint64()] = txLatency } } From 95cf31f59b27b195704952759a58a5aa34b2501c Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sun, 11 Dec 2022 13:42:43 -0500 Subject: [PATCH 16/20] ops(ci): renaming goreleaser --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 376f548e..f73bc29f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: goreleaser +name: polycli-releaser on: push: @@ -27,7 +27,7 @@ jobs: go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1 - + pwd make cross pushd out From 12884e7345d5a76b1e2222b5cc630518c6ab73b5 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Sun, 11 Dec 2022 16:07:34 -0500 Subject: [PATCH 17/20] fix: adding checks for contract deployment --- cmd/loadtest.go | 57 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 9a92dbe7..acd4df4b 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -573,19 +573,39 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro return err } currentNonce = currentNonce + 1 + err = blockUntilSuccessful(func() error { + _, err = erc20Contract.BalanceOf(cops, *ltp.FromETHAddress) + return err + }, 30) + if err != nil { + return err + } tops.Nonce = new(big.Int).SetUint64(currentNonce) tops.GasLimit = 10000000 tops = configureTransactOpts(tops) + _, err = erc20Contract.Mint(tops, new(big.Int).SetUint64(1_000_000_000_000)) + if err != nil { + log.Error().Err(err).Msg("There was an error minting ERC20") + return err + } + currentNonce = currentNonce + 1 err = blockUntilSuccessful(func() error { - _, err = erc20Contract.Mint(tops, new(big.Int).SetUint64(1_000_000_000_000)) - return err + var balance *big.Int + balance, err = erc20Contract.BalanceOf(cops, *ltp.FromETHAddress) + if err != nil { + return err + } + if balance.Uint64() == 0 { + err = fmt.Errorf("ERC20 Balance is Zero") + return err + } + return nil }, 30) if err != nil { return err } - currentNonce = currentNonce + 1 } var erc721Addr ethcommon.Address @@ -603,9 +623,16 @@ func mainLoop(ctx context.Context, c *ethclient.Client, rpc *ethrpc.Client) erro log.Error().Err(err).Msg("Unable to instantiate new erc20 contract") return err } - currentNonce = currentNonce + 1 + err = blockUntilSuccessful(func() error { + _, err = erc721Contract.BalanceOf(cops, *ltp.FromETHAddress) + return err + }, 30) + if err != nil { + return err + } + tops.Nonce = new(big.Int).SetUint64(currentNonce) tops.GasLimit = 10000000 tops = configureTransactOpts(tops) @@ -746,7 +773,7 @@ func blockUntilSuccessful(f func() error, tries int) error { log.Error().Err(err).Int("tries", waitCounter).Msg("Exhausted waiting period") return err } - log.Trace().Msg("Waiting to deploy") + log.Trace().Err(err).Msg("waiting for successful function execution") time.Sleep(time.Second) waitCounter = waitCounter - 1 continue @@ -1386,21 +1413,31 @@ func summarizeTransactions(ctx context.Context, c *ethclient.Client, rpc *ethrpc nonceTimes[ltr.Nonce] = ltr.RequestTime } + minLatency := time.Millisecond * 100 for _, bs := range blockData { for _, tx := range bs.Block.Transactions { // TODO: What happens when the system clock of the load tester isn't in sync with the system clock of the miner? // TODO: the timestamp in the chain only has granularity down to the second. How to deal with this - // the + 1 here is to deal with pretty obvious error margins due to the way block timestamp works. Not sure - // what the best way to account for this is. In the future we might want to determine what the min reasonable - // latency is... E.g. it's not possible to have a latency lower than the latency between our servers. Then we - // can find the min observed latency and shift all of the other latencies accordingly - mineTime := time.Unix(bs.Block.Timestamp.ToInt64()+1, 0) + mineTime := time.Unix(bs.Block.Timestamp.ToInt64(), 0) requestTime := nonceTimes[tx.Nonce.ToUint64()] txLatency := mineTime.Sub(requestTime) if txLatency.Hours() > 2 { log.Debug().Float64("txHours", txLatency.Hours()).Uint64("nonce", tx.Nonce.ToUint64()).Uint64("blockNumber", bs.Block.Number.ToUint64()).Time("mineTime", mineTime).Time("requestTime", requestTime).Msg("Encountered transaction with more than 2 hours latency") } bs.Latencies[tx.Nonce.ToUint64()] = txLatency + if txLatency < minLatency { + minLatency = txLatency + } + } + } + // TODO this might be a hack, but not sure what's a better way to deal with time discrepancies + if minLatency < time.Millisecond*100 { + log.Trace().Str("minLatency", minLatency.String()).Msg("minimum latency is below expected threshold") + shiftSize := ((time.Millisecond * 100) - minLatency) + time.Millisecond + 100 + for _, bs := range blockData { + for _, tx := range bs.Block.Transactions { + bs.Latencies[tx.Nonce.ToUint64()] += shiftSize + } } } From 79f28cbaf7588dee281427117f644f6ace612c78 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Mon, 12 Dec 2022 14:46:25 -0500 Subject: [PATCH 18/20] ops(ci): dropping lint Removing lint calls from Makefile to avoid running multiple times in the CI process https://github.com/maticnetwork/polygon-cli/pull/23#discussion_r1046070916 --- .github/workflows/release.yml | 3 --- Makefile | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f73bc29f..e8355d78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,9 +25,6 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu - go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1 - pwd make cross pushd out diff --git a/Makefile b/Makefile index e57d4c56..4d354cae 100644 --- a/Makefile +++ b/Makefile @@ -26,15 +26,15 @@ $(BUILD_DIR): ## Create the binary folder. mkdir -p $(BUILD_DIR) .PHONY: run -run: lint ## Run the go program. +run: ## Run the go program. go run main.go .PHONY: build -build: lint $(BUILD_DIR) ## Build go binary. +build: $(BUILD_DIR) ## Build go binary. go build -o $(BUILD_DIR)/$(BIN_NAME) main.go .PHONY: cross -cross: $(BUILD_DIR) lint ## Cross-compile go binaries using CGO. +cross: $(BUILD_DIR) ## Cross-compile go binaries using CGO. env CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "$(STATIC_LD_FLAGS)" -tags netgo -o $(BUILD_DIR)/linux-arm64-$(BIN_NAME) main.go env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(STATIC_LD_FLAGS)" -tags netgo -o $(BUILD_DIR)/linux-amd64-$(BIN_NAME) main.go # mac builds - this will be functional but will still have secp issues @@ -42,7 +42,7 @@ cross: $(BUILD_DIR) lint ## Cross-compile go binaries using CGO. env GOOS=darwin GOARCH=amd64 go build -ldflags "$(LD_FLAGS)" -tags netgo -o $(BUILD_DIR)/darwin-amd64-$(BIN_NAME) main.go .PHONY: simplecross -simplecross: $(BUILD_DIR) lint ## Cross-compile go binaries without using CGO. +simplecross: $(BUILD_DIR) ## Cross-compile go binaries without using CGO. env GOOS=linux GOARCH=arm64 go build -o $(BUILD_DIR)/linux-arm64-$(BIN_NAME) main.go env GOOS=darwin GOARCH=arm64 go build -o $(BUILD_DIR)/darwin-arm64-$(BIN_NAME) main.go env GOOS=linux GOARCH=amd64 go build -o $(BUILD_DIR)/linux-amd64-$(BIN_NAME) main.go From b2612d5f17fb5936bc22dc4ad4a777567ab6a90e Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Mon, 12 Dec 2022 15:15:18 -0500 Subject: [PATCH 19/20] docs: updating readme --- README.md | 12 ++++++++++++ cmd/loadtest.go | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d183e12..10b693ef 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,18 @@ The `--mode` flag is important for this command: - `c` Will call random functions in our load test contract - `f` will call a specific function on the load test contract. The function is specified using the `-f` flag +- `2` Will run an ERC20 transfer test. It starts out by minting + a large amount of an ERC20 contract then transferring it in small + amounts +- `7` Will run an ERC721 test which will mint an NFT over and over + again +- `i` Will call the increment function repeatedly on the load test + contract. It's a minimal example of a contract call that will + require an update to a contract's storage. +- `r` Will call any of th eother modes randomly +- `s` Is used for Avail / Eth to store random data in large amounts +- `l` Will call a smart contract function that runs as long as it can + (based on the block limit) This example is very simple. It runs 1000 requests at a max rate of 1 request per second against the http rpc endpoint on localhost. t's diff --git a/cmd/loadtest.go b/cmd/loadtest.go index acd4df4b..9ef1c880 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -927,9 +927,9 @@ func loadtestLong(ctx context.Context, c *ethclient.Client, nonce uint64, delega tops.GasLimit = 10000000 tops = configureTransactOpts(tops) - // TODO the deletgated call should be a parameter + // TODO the delegated call should be a parameter t1 = time.Now() - // loopBlockHashUntilLimit + // loopBlockHashUntilLimit (verify here https://abi.hashex.org/) _, err = delegatorContract.LoopDelegateCall(tops, ltAddress, []byte{0xa2, 0x71, 0xb7, 0x21}) // loopUntilLimit // _, err = delegatorContract.LoopDelegateCall(tops, ltAddress, []byte{0x65, 0x9b, 0xbb, 0x4f}) From a0942aaccede45ed6556a5cfe3ff42ff69cb14d0 Mon Sep 17 00:00:00 2001 From: John Hilliard Date: Mon, 12 Dec 2022 15:25:08 -0500 Subject: [PATCH 20/20] feat: putting block level dumping behind a verbosity check --- cmd/loadtest.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/loadtest.go b/cmd/loadtest.go index 9ef1c880..1fd5ba77 100644 --- a/cmd/loadtest.go +++ b/cmd/loadtest.go @@ -1474,16 +1474,19 @@ func printBlockSummary(bs map[uint64]blockSummary, startNonce, endNonce uint64) if gasUsed == 0 { blockUtilization = 0 } - _, _ = p.Printf("Block number: %v\tTime: %s\tGas Limit: %v\tGas Used: %v\tNum Tx: %v\tUtilization %v\tLatencies: %v\t%v\t%v\n", - number.Decimal(summary.Block.Number.ToUint64()), - time.Unix(summary.Block.Timestamp.ToInt64(), 0), - number.Decimal(summary.Block.GasLimit.ToUint64()), - number.Decimal(gasUsed), - number.Decimal(len(summary.Block.Transactions)), - number.Percent(blockUtilization), - number.Decimal(minLatency.Seconds()), - number.Decimal(medianLatency.Seconds()), - number.Decimal(maxLatency.Seconds())) + // if we're at trace, debug, or info level we'll output the block level metrics + if zerolog.GlobalLevel() <= zerolog.InfoLevel { + _, _ = p.Printf("Block number: %v\tTime: %s\tGas Limit: %v\tGas Used: %v\tNum Tx: %v\tUtilization %v\tLatencies: %v\t%v\t%v\n", + number.Decimal(summary.Block.Number.ToUint64()), + time.Unix(summary.Block.Timestamp.ToInt64(), 0), + number.Decimal(summary.Block.GasLimit.ToUint64()), + number.Decimal(gasUsed), + number.Decimal(len(summary.Block.Transactions)), + number.Percent(blockUtilization), + number.Decimal(minLatency.Seconds()), + number.Decimal(medianLatency.Seconds()), + number.Decimal(maxLatency.Seconds())) + } totalTransactions += uint64(len(summary.Block.Transactions)) totalGasUsed += gasUsed }