diff --git a/contracts/src/ERC721.sol b/contracts/src/ERC721.sol new file mode 100644 index 000000000..a4c083f2a --- /dev/null +++ b/contracts/src/ERC721.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +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 _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); + } +} + +contract MyNFT is ERC721 { + function mint(address to, uint id) external { + _mint(to, id); + } + + function burn(uint id) external { + require(msg.sender == _ownerOf[id], "not owner"); + _burn(id); + } +} diff --git a/contracts/test/ERC721Test.js b/contracts/test/ERC721Test.js new file mode 100644 index 000000000..32347e757 --- /dev/null +++ b/contracts/test/ERC721Test.js @@ -0,0 +1,38 @@ +const { ethers, upgrades } = require('hardhat'); + +describe("ERC721 test", function () { + + describe("ERC721 Throughput", function () { + let erc721; + let owner; + let receiver; + + // This function deploys a new instance of the contract before each test + beforeEach(async function () { + let signers = await ethers.getSigners(); + owner = signers[0]; + receiver = signers[1]; + const ERC721 = await ethers.getContractFactory("MyNFT") + erc721 = await ERC721.deploy(); + + await Promise.all([erc721.waitForDeployment()]) + }); + + it("should send 10000", async function(){ + this.timeout(100000); // Increase timeout for this test + + let nonce = await ethers.provider.getTransactionCount(owner.address); + const sends = [] + + const count = 10000 + // start of all the rpc calls + for(let i=0; i /dev/null + +git submodule update --init --recursive > /dev/null + +/root/.foundry/bin/forge create -r "$evm_endpoint" --private-key 57acb95d82739866a5c29e40b0aa2590742ae50425b7dd5b5d279a986370189e src/ERC721.sol:MyNFT --json | jq -r '.deployedTo' diff --git a/loadtest/contracts/evm/bindings/erc721/abi.json b/loadtest/contracts/evm/bindings/erc721/abi.json new file mode 100644 index 000000000..096d6beb4 --- /dev/null +++ b/loadtest/contracts/evm/bindings/erc721/abi.json @@ -0,0 +1,318 @@ +[ + { + "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": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "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/loadtest/contracts/evm/bindings/erc721/erc721.go b/loadtest/contracts/evm/bindings/erc721/erc721.go new file mode 100644 index 000000000..fb90452d8 --- /dev/null +++ b/loadtest/contracts/evm/bindings/erc721/erc721.go @@ -0,0 +1,961 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc721 + +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\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"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\"}]", +} + +// Erc721ABI is the input ABI used to generate the binding from. +// Deprecated: Use Erc721MetaData.ABI instead. +var Erc721ABI = Erc721MetaData.ABI + +// 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) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 id) returns() +func (_Erc721 *Erc721Transactor) Burn(opts *bind.TransactOpts, id *big.Int) (*types.Transaction, error) { + return _Erc721.contract.Transact(opts, "burn", id) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 id) returns() +func (_Erc721 *Erc721Session) Burn(id *big.Int) (*types.Transaction, error) { + return _Erc721.Contract.Burn(&_Erc721.TransactOpts, id) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 id) returns() +func (_Erc721 *Erc721TransactorSession) Burn(id *big.Int) (*types.Transaction, error) { + return _Erc721.Contract.Burn(&_Erc721.TransactOpts, 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/loadtest/contracts/evm/src/ERC721.sol b/loadtest/contracts/evm/src/ERC721.sol new file mode 100644 index 000000000..d35102496 --- /dev/null +++ b/loadtest/contracts/evm/src/ERC721.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +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"); + // remove this check so that it's repeatable + // require(_ownerOf[id] == address(0), "already minted"); + + _balanceOf[to]++; + _ownerOf[id] = to; + + emit Transfer(address(0), 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); + } +} + +contract MyNFT is ERC721 { + function mint(address to, uint id) external { + _mint(to, id); + } + + function burn(uint id) external { + require(msg.sender == _ownerOf[id], "not owner"); + _burn(id); + } +} diff --git a/loadtest/evm.go b/loadtest/evm.go index 790f7ca84..48ad16f5e 100644 --- a/loadtest/evm.go +++ b/loadtest/evm.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/sei-protocol/sei-chain/loadtest/contracts/evm/bindings/erc20" + "github.com/sei-protocol/sei-chain/loadtest/contracts/evm/bindings/erc721" ) type EvmTxClient struct { @@ -77,6 +78,8 @@ func (txClient *EvmTxClient) GetTxForMsgType(msgType string) *ethtypes.Transacti return txClient.GenerateSendFundsTx() case ERC20: return txClient.GenerateERC20TransferTx() + case ERC721: + return txClient.GenerateERC721Mint() default: panic("invalid message type") } @@ -118,6 +121,22 @@ func (txClient *EvmTxClient) GenerateERC20TransferTx() *ethtypes.Transaction { return tx } +func (txClient *EvmTxClient) GenerateERC721Mint() *ethtypes.Transaction { + opts := txClient.getTransactOpts() + // override gas limit for an ERC20 transfer + opts.GasLimit = uint64(100000) + tokenAddress := txClient.evmAddresses.ERC721 + token, err := erc721.NewErc721(tokenAddress, GetNextEthClient(txClient.ethClients)) + if err != nil { + panic(fmt.Sprintf("Failed to create ERC721 contract: %v \n", err)) + } + tx, err := token.Mint(opts, txClient.accountAddress, randomValue()) + if err != nil { + panic(fmt.Sprintf("Failed to create ERC20 transfer: %v \n", err)) + } + return tx +} + func (txClient *EvmTxClient) getTransactOpts() *bind.TransactOpts { auth, err := bind.NewKeyedTransactorWithChainID(txClient.privateKey, txClient.chainId) if err != nil { diff --git a/loadtest/loadtest_client.go b/loadtest/loadtest_client.go index a876e66a6..41d19a2a8 100644 --- a/loadtest/loadtest_client.go +++ b/loadtest/loadtest_client.go @@ -53,7 +53,7 @@ func NewLoadTestClient(config Config) *LoadTestClient { txClients, grpcConns := BuildGrpcClients(config) var evmTxClients []*EvmTxClient if config.EvmRpcEndpoints != "" { - if config.ContainsAnyMessageTypes(EVM, ERC20) { + if config.ContainsAnyMessageTypes(EVM, ERC20, ERC721) { evmTxClients = BuildEvmTxClients(config, keys) } } @@ -191,7 +191,7 @@ func (c *LoadTestClient) BuildTxs( var signedTx SignedTx // Sign EVM and Cosmos TX differently switch messageType { - case EVM, ERC20: + case EVM, ERC20, ERC721: signedTx = SignedTx{EvmTx: c.generateSignedEvmTx(keyIndex, messageType)} default: signedTx = SignedTx{TxBytes: c.generateSignedCosmosTxs(keyIndex, messageType, producedCount)} diff --git a/loadtest/main.go b/loadtest/main.go index 20b7ec23f..b32e7abc1 100644 --- a/loadtest/main.go +++ b/loadtest/main.go @@ -100,9 +100,16 @@ func deployEvmContracts(config *Config) { fmt.Println("error deploying, make sure 0xF87A299e6bC7bEba58dbBe5a5Aa21d49bCD16D52 is funded") panic(err) } - config.EVMAddresses = &EVMAddresses{ - ERC20: erc20, + config.EVMAddresses.ERC20 = erc20 + } + if config.ContainsAnyMessageTypes(ERC721) { + fmt.Println("Deploying ERC721 contract") + erc721, err := deployEvmContract("loadtest/contracts/deploy_erc721.sh", config) + if err != nil { + fmt.Println("error deploying, make sure 0xF87A299e6bC7bEba58dbBe5a5Aa21d49bCD16D52 is funded") + panic(err) } + config.EVMAddresses.ERC721 = erc721 } } diff --git a/loadtest/types.go b/loadtest/types.go index 720cd9775..b1386d648 100644 --- a/loadtest/types.go +++ b/loadtest/types.go @@ -19,6 +19,7 @@ const ( Bank string = "bank" EVM string = "evm" ERC20 string = "erc20" + ERC721 string = "erc721" CollectRewards string = "collect_rewards" DistributeRewards string = "distribute_rewards" FailureBankMalformed string = "failure_bank_malformed" @@ -36,7 +37,8 @@ const ( ) type EVMAddresses struct { - ERC20 common.Address + ERC20 common.Address + ERC721 common.Address } type Config struct {