From 2eb91a6eb14f6dd5783fe047ebfecaec45046c03 Mon Sep 17 00:00:00 2001 From: KallyDev Date: Wed, 22 Nov 2023 19:48:12 +0800 Subject: [PATCH 1/6] feat(provider/ethereum): RPC client for the execution layer --- go.mod | 1 + go.sum | 2 + provider/ethereum/client.go | 157 +++ provider/ethereum/contract/detector.go | 163 +++ provider/ethereum/contract/detector_test.go | 171 +++ .../ethereum/contract/erc1155/abi/ERC1155.abi | 351 ++++++ .../ethereum/contract/erc1155/contract.go | 17 + .../contract/erc1155/contract_erc1155.go | 1088 +++++++++++++++++ .../ethereum/contract/erc165/abi/ERC165.abi | 21 + provider/ethereum/contract/erc165/contract.go | 4 + .../contract/erc165/contract_erc165.go | 212 ++++ .../ethereum/contract/erc20/abi/ERC20.abi | 288 +++++ provider/ethereum/contract/erc20/contract.go | 15 + .../ethereum/contract/erc20/contract_erc20.go | 780 ++++++++++++ .../ethereum/contract/erc721/abi/ERC721.abi | 348 ++++++ provider/ethereum/contract/erc721/contract.go | 17 + .../contract/erc721/contract_erc721.go | 1012 +++++++++++++++ provider/ethereum/contract/selector.go | 69 ++ provider/ethereum/contract/selector_test.go | 60 + provider/ethereum/contract/standard.go | 13 + provider/ethereum/contract/standard_string.go | 177 +++ provider/ethereum/proxy/proxy.go | 63 + provider/ethereum/proxy/proxy_test.go | 64 + provider/ethereum/type.go | 197 +++ provider/ethereum/type_block.go | 92 ++ provider/ethereum/type_header.go | 92 ++ provider/ethereum/type_log.go | 86 ++ provider/ethereum/type_receipt.go | 92 ++ provider/ethereum/type_transaction.go | 104 ++ provider/ethereum/type_transaction_call.go | 68 ++ 30 files changed, 5824 insertions(+) create mode 100644 provider/ethereum/client.go create mode 100644 provider/ethereum/contract/detector.go create mode 100644 provider/ethereum/contract/detector_test.go create mode 100644 provider/ethereum/contract/erc1155/abi/ERC1155.abi create mode 100644 provider/ethereum/contract/erc1155/contract.go create mode 100644 provider/ethereum/contract/erc1155/contract_erc1155.go create mode 100644 provider/ethereum/contract/erc165/abi/ERC165.abi create mode 100644 provider/ethereum/contract/erc165/contract.go create mode 100644 provider/ethereum/contract/erc165/contract_erc165.go create mode 100644 provider/ethereum/contract/erc20/abi/ERC20.abi create mode 100644 provider/ethereum/contract/erc20/contract.go create mode 100644 provider/ethereum/contract/erc20/contract_erc20.go create mode 100644 provider/ethereum/contract/erc721/abi/ERC721.abi create mode 100644 provider/ethereum/contract/erc721/contract.go create mode 100644 provider/ethereum/contract/erc721/contract_erc721.go create mode 100644 provider/ethereum/contract/selector.go create mode 100644 provider/ethereum/contract/selector_test.go create mode 100644 provider/ethereum/contract/standard.go create mode 100644 provider/ethereum/contract/standard_string.go create mode 100644 provider/ethereum/proxy/proxy.go create mode 100644 provider/ethereum/proxy/proxy_test.go create mode 100644 provider/ethereum/type.go create mode 100644 provider/ethereum/type_block.go create mode 100644 provider/ethereum/type_header.go create mode 100644 provider/ethereum/type_log.go create mode 100644 provider/ethereum/type_receipt.go create mode 100644 provider/ethereum/type_transaction.go create mode 100644 provider/ethereum/type_transaction_call.go diff --git a/go.mod b/go.mod index e928318e..b3c29b76 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.2.3 // indirect diff --git a/go.sum b/go.sum index 57daa7fb..6cc853cd 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFA github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/provider/ethereum/client.go b/provider/ethereum/client.go new file mode 100644 index 00000000..b7589cf1 --- /dev/null +++ b/provider/ethereum/client.go @@ -0,0 +1,157 @@ +package ethereum + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" +) + +// Client provides basic RPC methods. +type Client interface { + // ContractCaller is used for compatibility with abigen generated contract callers. + bind.ContractCaller + + ChainID(ctx context.Context) (*big.Int, error) + BlockNumber(ctx context.Context) (*big.Int, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*Header, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*Header, error) + BlockByHash(ctx context.Context, hash common.Hash) (*Block, error) + BlockByNumber(ctx context.Context, number *big.Int) (*Block, error) + BlockReceipts(ctx context.Context, number *big.Int) ([]*Receipt, error) + TransactionByHash(ctx context.Context, hash common.Hash) (*Transaction, error) + TransactionReceipt(ctx context.Context, hash common.Hash) (*Receipt, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) +} + +var _ Client = (*client)(nil) + +type client struct { + rpcClient *rpc.Client +} + +func (c *client) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + var code hexutil.Bytes + if err := c.rpcClient.CallContext(ctx, &code, "eth_getCode", contract, formatBlockNumber(blockNumber)); err != nil { + return nil, err + } + + return code, nil +} + +func (c *client) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + var value hexutil.Bytes + if err := c.rpcClient.CallContext(ctx, &value, "eth_call", formatTransactionCall(call), formatBlockNumber(blockNumber)); err != nil { + return nil, err + } + + return value, nil +} + +func (c *client) ChainID(ctx context.Context) (*big.Int, error) { + var chainID *hexutil.Big + if err := c.rpcClient.CallContext(ctx, &chainID, "eth_chainId"); err != nil { + return nil, err + } + + return chainID.ToInt(), nil +} + +func (c *client) BlockNumber(ctx context.Context) (*big.Int, error) { + var number *hexutil.Big + if err := c.rpcClient.CallContext(ctx, &number, "eth_blockNumber"); err != nil { + return nil, err + } + + return number.ToInt(), nil +} + +func (c *client) HeaderByHash(ctx context.Context, hash common.Hash) (*Header, error) { + var header Header + if err := c.rpcClient.CallContext(ctx, &header, "eth_getBlockByHash", hash, false); err != nil { + return nil, err + } + + return &header, nil +} + +func (c *client) HeaderByNumber(ctx context.Context, number *big.Int) (*Header, error) { + var header Header + if err := c.rpcClient.CallContext(ctx, &header, "eth_getBlockByNumber", formatBlockNumber(number), false); err != nil { + return nil, err + } + + return &header, nil +} + +func (c *client) BlockByHash(ctx context.Context, hash common.Hash) (*Block, error) { + var block Block + if err := c.rpcClient.CallContext(ctx, &block, "eth_getBlockByHash", hash, true); err != nil { + return nil, err + } + + return &block, nil +} + +func (c *client) BlockByNumber(ctx context.Context, number *big.Int) (*Block, error) { + var block Block + if err := c.rpcClient.CallContext(ctx, &block, "eth_getBlockByNumber", formatBlockNumber(number), true); err != nil { + return nil, err + } + + return &block, nil +} + +func (c *client) BlockReceipts(ctx context.Context, number *big.Int) ([]*Receipt, error) { + var receipts []*Receipt + if err := c.rpcClient.CallContext(ctx, &receipts, "eth_getBlockReceipts", formatBlockNumber(number)); err != nil { + return nil, err + } + + return receipts, nil +} + +func (c *client) TransactionByHash(ctx context.Context, hash common.Hash) (*Transaction, error) { + var transaction Transaction + if err := c.rpcClient.CallContext(ctx, &transaction, "eth_getTransactionByHash", hash); err != nil { + return nil, err + } + + return &transaction, nil +} + +func (c *client) TransactionReceipt(ctx context.Context, hash common.Hash) (*Receipt, error) { + var receipt Receipt + if err := c.rpcClient.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash); err != nil { + return nil, err + } + + return &receipt, nil +} + +func (c *client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + var value hexutil.Bytes + if err := c.rpcClient.CallContext(ctx, &value, "eth_getStorageAt", account, key, formatBlockNumber(blockNumber)); err != nil { + return nil, err + } + + return value, nil +} + +// Dial creates a new client for the given endpoint. +func Dial(ctx context.Context, endpoint string) (Client, error) { + rpcClient, err := rpc.DialContext(ctx, endpoint) + if err != nil { + return nil, err + } + + instance := client{ + rpcClient: rpcClient, + } + + return &instance, nil +} diff --git a/provider/ethereum/contract/detector.go b/provider/ethereum/contract/detector.go new file mode 100644 index 00000000..7c896e06 --- /dev/null +++ b/provider/ethereum/contract/detector.go @@ -0,0 +1,163 @@ +package contract + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc165" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/proxy" +) + +var ( + // InterfaceIDERC721 method ID from https://eips.ethereum.org/EIPS/eip-721#:~:text=0x80ac58cd. + InterfaceIDERC721 = [4]byte(hexutil.MustDecode("0x80ac58cd")) + // InterfaceIDERC1155 method ID from https://eips.ethereum.org/EIPS/eip-1155#:~:text=0xd9b67a26. + InterfaceIDERC1155 = [4]byte(hexutil.MustDecode("0xd9b67a26")) +) + +var exceptionalContracts = map[uint64]map[common.Address]Standard{} + +// DetectTokenStandard detects the token standard of the contract by bytecode. +// It supports the ERC-1967 proxy contract. +func DetectTokenStandard(ctx context.Context, chainID *big.Int, address common.Address, blockNumber *big.Int, ethereumClient ethereum.Client) (Standard, error) { + if addressMap, exists := exceptionalContracts[chainID.Uint64()]; exists { + if standard, exists := addressMap[address]; exists { + return standard, nil + } + } + + code, err := ethereumClient.CodeAt(ctx, address, blockNumber) + if err != nil { + return StandardUnknown, fmt.Errorf("get code: %w", err) + } + + // If the contract is ERC-1967, get the implementation contract first + if DetectERC1967WithCode(chainID, address, code) { + implementation, err := proxy.GetImplementation(ctx, address, blockNumber, ethereumClient) + if err != nil { + return StandardUnknown, fmt.Errorf("get %s implementation: %w", address, err) + } + + if code, err = ethereumClient.CodeAt(ctx, *implementation, blockNumber); err != nil { + return StandardUnknown, fmt.Errorf("get %s code: %w", implementation, err) + } + } + + // It can cover most ERC-20 tokens + if DetectERC20WithCode(chainID, address, code) { + return StandardERC20, nil + } + + // The result may be influenced by compiler optimization + if DetectERC721WithCode(chainID, address, code) { + return StandardERC721, nil + } + + // The result may be influenced by compiler optimization + if DetectERC1155WithCode(chainID, address, code) { + return StandardERC1155, nil + } + + // Precise determination of contract criteria by Interface ID, but will require RPC requests + if DetectERC165WithCode(chainID, address, code) { + return DetectNFTStandard(ctx, chainID, address, blockNumber, ethereumClient) + } + + // If ERC-165 is not implemented, it is not a standard NFT contract + return StandardUnknown, nil +} + +// DetectNFTStandard detects the NFT standard of the contract by ERC-165. +func DetectNFTStandard(ctx context.Context, _ *big.Int, address common.Address, blockNumber *big.Int, ethereumClient ethereum.Client) (Standard, error) { + caller, err := erc165.NewERC165Caller(address, ethereumClient) + if err != nil { + return StandardUnknown, fmt.Errorf("initialize ERC-165 caller: %w", err) + } + + callOptions := bind.CallOpts{ + BlockNumber: blockNumber, + Context: ctx, + } + + isERC721, err := caller.SupportsInterface(&callOptions, InterfaceIDERC721) + if err != nil { + return StandardUnknown, fmt.Errorf("detect ERC-721 interface: %w", err) + } + + if isERC721 { + return StandardERC721, nil + } + + isERC1155, err := caller.SupportsInterface(&callOptions, InterfaceIDERC1155) + if err != nil { + return StandardUnknown, fmt.Errorf("detect ERC-1155 interface: %w", err) + } + + if isERC1155 { + return StandardERC1155, nil + } + + return StandardUnknown, nil +} + +// DetectERC20WithCode detects if bytecode of the contract is ERC-20. +func DetectERC20WithCode(_ *big.Int, address common.Address, code []byte) bool { + exceptionalAddresses := map[common.Address]bool{} + + return exceptionalAddresses[address] || ContainsMethodIDs(code, + MethodID("name()"), + MethodID("symbol()"), + MethodID("decimals()"), + MethodID("totalSupply()"), + MethodID("decimals()"), + MethodID("balanceOf(address)"), + MethodID("transfer(address,uint256)"), + MethodID("transferFrom(address,address,uint256)"), + MethodID("approve(address,uint256)"), + MethodID("allowance(address,address)"), + MethodID("Transfer(address,address,uint256)"), + MethodID("Approval(address,address,uint256)"), + ) +} + +// DetectERC165WithCode detects if bytecode of the contract is ERC-165. +func DetectERC165WithCode(_ *big.Int, _ common.Address, code []byte) bool { + return ContainsMethodIDs(code, MethodID("supportsInterface(bytes4)")) +} + +// DetectERC721WithCode detects if bytecode of the contract is ERC-721. +func DetectERC721WithCode(_ *big.Int, _ common.Address, code []byte) bool { + return ContainsMethodIDs(code, // 0x80ac58cd + MethodID("balanceOf(address)"), + MethodID("ownerOf(uint256)"), + MethodID("approve(address,uint256)"), + MethodID("getApproved(uint256)"), + MethodID("setApprovalForAll(address,bool)"), + MethodID("isApprovedForAll(address,address)"), + MethodID("transferFrom(address,address,uint256)"), + MethodID("safeTransferFrom(address,address,uint256)"), + MethodID("safeTransferFrom(address,address,uint256,bytes)"), + ) +} + +// DetectERC1155WithCode detects if bytecode of the contract is ERC-1155. +func DetectERC1155WithCode(_ *big.Int, _ common.Address, code []byte) bool { + return ContainsMethodIDs(code, // 0xd9b67a26 + MethodID("balanceOf(address,uint256)"), + MethodID("balanceOfBatch(address[],uint256[])"), + MethodID("setApprovalForAll(address,bool)"), + MethodID("isApprovedForAll(address,address)"), + MethodID("safeTransferFrom(address,address,uint256,uint256,bytes)"), + MethodID("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"), + ) +} + +// DetectERC1967WithCode detects if bytecode of the contract is ERC-1967. +func DetectERC1967WithCode(_ *big.Int, _ common.Address, code []byte) bool { + return ContainsMethodIDs(code, MethodID("implementation()")) +} diff --git a/provider/ethereum/contract/detector_test.go b/provider/ethereum/contract/detector_test.go new file mode 100644 index 00000000..e92f6860 --- /dev/null +++ b/provider/ethereum/contract/detector_test.go @@ -0,0 +1,171 @@ +package contract_test + +import ( + "context" + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/endpoint" + "github.com/naturalselectionlabs/rss3-node/schema/filter" + "github.com/stretchr/testify/require" +) + +func TestDetectTokenStandard(t *testing.T) { + t.Parallel() + + type arguments struct { + ctx context.Context + chain filter.ChainEthereum + address common.Address + } + + testcases := []struct { + name string + arguments arguments + want contract.Standard + wantError require.ErrorAssertionFunc + }{ + { + name: "RSS3", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0xc98d64da73a6616c42117b582e832812e7b8d57f + address: common.HexToAddress("0xc98D64DA73a6616c42117b582e832812e7B8D57F"), + }, + want: contract.StandardERC20, + wantError: require.NoError, + }, + { + name: "USD Coin", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 + address: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), + }, + want: contract.StandardERC20, + wantError: require.NoError, + }, + { + name: "Maker", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2 + address: common.HexToAddress("0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2"), + }, + want: contract.StandardERC20, + wantError: require.NoError, + }, + { + name: "The Genesis RSS3 Avatar NFT", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + address: common.HexToAddress("0x5452C7fB99D99fAb3Cc1875E9DA9829Cb50F7A13"), + }, + want: contract.StandardERC721, + wantError: require.NoError, + }, + { + name: "ENS", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + address: common.HexToAddress("0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85"), + }, + want: contract.StandardERC721, + wantError: require.NoError, + }, + { + name: "Proof Of Stake Pages", // SBT + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0x5bf5bcc5362f88721167c1068b58c60cad075aac + address: common.HexToAddress("0x5bF5BCc5362F88721167C1068b58C60caD075aAc"), + }, + want: contract.StandardERC721, + wantError: require.NoError, + }, + { + name: "Love, Death + Robots", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0xFD43D1dA000558473822302e1d44D81dA2e4cC0d + address: common.HexToAddress("0xFD43D1dA000558473822302e1d44D81dA2e4cC0d"), + }, + want: contract.StandardERC1155, + wantError: require.NoError, + }, + { + name: "TIME NFT Special Issues", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0x8442864d6AB62a9193be2F16580c08E0D7BCda2f + address: common.HexToAddress("0x8442864d6AB62a9193be2F16580c08E0D7BCda2f"), + }, + want: contract.StandardERC1155, + wantError: require.NoError, + }, + { + name: "Beacon Deposit Contract", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa + address: common.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa"), + }, + want: contract.StandardUnknown, + wantError: require.NoError, + }, + { + name: "Arbitrum Bridge", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + // https://etherscan.io/address/0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a + address: common.HexToAddress("0x8315177aB297bA92A06054cE80a67Ed4DBd7ed3a"), + }, + want: contract.StandardUnknown, + wantError: require.NoError, + }, + } + + for _, testcase := range testcases { + testcase := testcase + + t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + + chainID := new(big.Int).SetUint64(testcase.arguments.chain.ID()) + + ethereumClient, err := ethereum.Dial(testcase.arguments.ctx, endpoint.MustGet(testcase.arguments.chain)) + testcase.wantError(t, err) + + result, err := contract.DetectTokenStandard(testcase.arguments.ctx, chainID, testcase.arguments.address, nil, ethereumClient) + testcase.wantError(t, err) + + require.Equal(t, result, testcase.want) + }) + } +} + +func BenchmarkDetectERC20WithCode(b *testing.B) { + // RSS3 Token + chainID := new(big.Int).SetUint64(filter.ChainEthereumMainnet.ID()) + address := common.HexToAddress("0xc98D64DA73a6616c42117b582e832812e7B8D57F") + code, err := hex.DecodeString("608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461012957806370a082311461013c57806395d89b411461014f578063a457c2d714610157578063a9059cbb1461016a578063dd62ed3e1461017d576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ec57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b6610190565b6040516100c391906106dd565b60405180910390f35b6100df6100da3660046106a9565b610222565b6040516100c391906106d2565b6100f461023f565b6040516100c39190610911565b6100df61010f36600461066e565b610245565b61011c6102de565b6040516100c3919061091a565b6100df6101373660046106a9565b6102e3565b6100f461014a36600461061b565b610337565b6100b6610356565b6100df6101653660046106a9565b610365565b6100df6101783660046106a9565b6103de565b6100f461018b36600461063c565b6103f2565b60606003805461019f9061094c565b80601f01602080910402602001604051908101604052809291908181526020018280546101cb9061094c565b80156102185780601f106101ed57610100808354040283529160200191610218565b820191906000526020600020905b8154815290600101906020018083116101fb57829003601f168201915b5050505050905090565b600061023661022f61041d565b8484610421565b50600192915050565b60025490565b60006102528484846104d5565b6001600160a01b03841660009081526001602052604081208161027361041d565b6001600160a01b03166001600160a01b03168152602001908152602001600020549050828110156102bf5760405162461bcd60e51b81526004016102b6906107fb565b60405180910390fd5b6102d3856102cb61041d565b858403610421565b506001949350505050565b601290565b60006102366102f061041d565b8484600160006102fe61041d565b6001600160a01b03908116825260208083019390935260409182016000908120918b16815292529020546103329190610928565b610421565b6001600160a01b0381166000908152602081905260409020545b919050565b60606004805461019f9061094c565b6000806001600061037461041d565b6001600160a01b03908116825260208083019390935260409182016000908120918816815292529020549050828110156103c05760405162461bcd60e51b81526004016102b6906108cc565b6103d46103cb61041d565b85858403610421565b5060019392505050565b60006102366103eb61041d565b84846104d5565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b0383166104475760405162461bcd60e51b81526004016102b690610888565b6001600160a01b03821661046d5760405162461bcd60e51b81526004016102b690610773565b6001600160a01b0380841660008181526001602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906104c8908590610911565b60405180910390a3505050565b6001600160a01b0383166104fb5760405162461bcd60e51b81526004016102b690610843565b6001600160a01b0382166105215760405162461bcd60e51b81526004016102b690610730565b61052c8383836105ff565b6001600160a01b038316600090815260208190526040902054818110156105655760405162461bcd60e51b81526004016102b6906107b5565b6001600160a01b0380851660009081526020819052604080822085850390559185168152908120805484929061059c908490610928565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105e69190610911565b60405180910390a36105f98484846105ff565b50505050565b505050565b80356001600160a01b038116811461035157600080fd5b60006020828403121561062c578081fd5b61063582610604565b9392505050565b6000806040838503121561064e578081fd5b61065783610604565b915061066560208401610604565b90509250929050565b600080600060608486031215610682578081fd5b61068b84610604565b925061069960208501610604565b9150604084013590509250925092565b600080604083850312156106bb578182fd5b6106c483610604565b946020939093013593505050565b901515815260200190565b6000602080835283518082850152825b81811015610709578581018301518582016040015282016106ed565b8181111561071a5783604083870101525b50601f01601f1916929092016040019392505050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201526265737360e81b606082015260800190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604082015261737360f01b606082015260800190565b60208082526026908201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604082015265616c616e636560d01b606082015260800190565b60208082526028908201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616040820152676c6c6f77616e636560c01b606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604082015264647265737360d81b606082015260800190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526025908201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604082015264207a65726f60d81b606082015260800190565b90815260200190565b60ff91909116815260200190565b6000821982111561094757634e487b7160e01b81526011600452602481fd5b500190565b60028104600182168061096057607f821691505b6020821081141561098157634e487b7160e01b600052602260045260246000fd5b5091905056fea2646970667358221220dce3469df9bbc6af8b36a7047024a2509da1d09910cf2bdb5ec57acc4d7031b564736f6c63430008000033") + require.NoError(b, err) + + for i := 0; i < b.N; i++ { + require.False(b, contract.DetectERC165WithCode(chainID, address, code)) + } +} diff --git a/provider/ethereum/contract/erc1155/abi/ERC1155.abi b/provider/ethereum/contract/erc1155/abi/ERC1155.abi new file mode 100644 index 00000000..0d32350d --- /dev/null +++ b/provider/ethereum/contract/erc1155/abi/ERC1155.abi @@ -0,0 +1,351 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "uri_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "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": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "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": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "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": "uint256", + "name": "amount", + "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": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "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" + } +] diff --git a/provider/ethereum/contract/erc1155/contract.go b/provider/ethereum/contract/erc1155/contract.go new file mode 100644 index 00000000..415bb5cb --- /dev/null +++ b/provider/ethereum/contract/erc1155/contract.go @@ -0,0 +1,17 @@ +package erc1155 + +import "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + +// https://eips.ethereum.org/EIPS/eip-1155 +//go:generate go run --mod=mod github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 --abi ./abi/ERC1155.abi --pkg erc1155 --type ERC1155 --out contract_erc1155.go + +var ( + MethodIDSafeTransferFrom = contract.MethodID("safeTransferFrom(address,address,uint256,uint256,bytes)") + MethodIDSafeBatchTransferFrom = contract.MethodID("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)") + MethodIDSetApprovalForAll = contract.MethodID("setApprovalForAll(address,bool)") + + EventHashTransferSingle = contract.EventHash("TransferSingle(address,address,address,uint256,uint256)") + EventHashTransferBatch = contract.EventHash("TransferBatch(address,address,address,uint256[],uint256[])") + EventHashApprovalForAll = contract.EventHash("ApprovalForAll(address,address,bool)") + EventHashURI = contract.EventHash("URI(string,uint256)") +) diff --git a/provider/ethereum/contract/erc1155/contract_erc1155.go b/provider/ethereum/contract/erc1155/contract_erc1155.go new file mode 100644 index 00000000..05aa44d9 --- /dev/null +++ b/provider/ethereum/contract/erc1155/contract_erc1155.go @@ -0,0 +1,1088 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc1155 + +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 +) + +// ERC1155MetaData contains all meta data concerning the ERC1155 contract. +var ERC1155MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"uri_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"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\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"name\":\"TransferBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"TransferSingle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"URI\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"}],\"name\":\"balanceOfBatch\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"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\":\"ids\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeBatchTransferFrom\",\"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\":\"uint256\",\"name\":\"amount\",\"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\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"uri\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"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\"}]", +} + +// ERC1155ABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC1155MetaData.ABI instead. +var ERC1155ABI = ERC1155MetaData.ABI + +// ERC1155 is an auto generated Go binding around an Ethereum contract. +type ERC1155 struct { + ERC1155Caller // Read-only binding to the contract + ERC1155Transactor // Write-only binding to the contract + ERC1155Filterer // Log filterer for contract events +} + +// ERC1155Caller is an auto generated read-only Go binding around an Ethereum contract. +type ERC1155Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1155Transactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC1155Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1155Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC1155Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1155Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC1155Session struct { + Contract *ERC1155 // 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 +} + +// ERC1155CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC1155CallerSession struct { + Contract *ERC1155Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC1155TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC1155TransactorSession struct { + Contract *ERC1155Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC1155Raw is an auto generated low-level Go binding around an Ethereum contract. +type ERC1155Raw struct { + Contract *ERC1155 // Generic contract binding to access the raw methods on +} + +// ERC1155CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC1155CallerRaw struct { + Contract *ERC1155Caller // Generic read-only contract binding to access the raw methods on +} + +// ERC1155TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC1155TransactorRaw struct { + Contract *ERC1155Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC1155 creates a new instance of ERC1155, bound to a specific deployed contract. +func NewERC1155(address common.Address, backend bind.ContractBackend) (*ERC1155, error) { + contract, err := bindERC1155(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC1155{ERC1155Caller: ERC1155Caller{contract: contract}, ERC1155Transactor: ERC1155Transactor{contract: contract}, ERC1155Filterer: ERC1155Filterer{contract: contract}}, nil +} + +// NewERC1155Caller creates a new read-only instance of ERC1155, bound to a specific deployed contract. +func NewERC1155Caller(address common.Address, caller bind.ContractCaller) (*ERC1155Caller, error) { + contract, err := bindERC1155(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC1155Caller{contract: contract}, nil +} + +// NewERC1155Transactor creates a new write-only instance of ERC1155, bound to a specific deployed contract. +func NewERC1155Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC1155Transactor, error) { + contract, err := bindERC1155(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC1155Transactor{contract: contract}, nil +} + +// NewERC1155Filterer creates a new log filterer instance of ERC1155, bound to a specific deployed contract. +func NewERC1155Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC1155Filterer, error) { + contract, err := bindERC1155(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC1155Filterer{contract: contract}, nil +} + +// bindERC1155 binds a generic wrapper to an already deployed contract. +func bindERC1155(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC1155MetaData.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 (_ERC1155 *ERC1155Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC1155.Contract.ERC1155Caller.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 (_ERC1155 *ERC1155Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC1155.Contract.ERC1155Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC1155 *ERC1155Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC1155.Contract.ERC1155Transactor.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 (_ERC1155 *ERC1155CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC1155.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 (_ERC1155 *ERC1155TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC1155.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC1155 *ERC1155TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC1155.Contract.contract.Transact(opts, method, params...) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x00fdd58e. +// +// Solidity: function balanceOf(address account, uint256 id) view returns(uint256) +func (_ERC1155 *ERC1155Caller) BalanceOf(opts *bind.CallOpts, account common.Address, id *big.Int) (*big.Int, error) { + var out []interface{} + err := _ERC1155.contract.Call(opts, &out, "balanceOf", account, id) + + 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 0x00fdd58e. +// +// Solidity: function balanceOf(address account, uint256 id) view returns(uint256) +func (_ERC1155 *ERC1155Session) BalanceOf(account common.Address, id *big.Int) (*big.Int, error) { + return _ERC1155.Contract.BalanceOf(&_ERC1155.CallOpts, account, id) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x00fdd58e. +// +// Solidity: function balanceOf(address account, uint256 id) view returns(uint256) +func (_ERC1155 *ERC1155CallerSession) BalanceOf(account common.Address, id *big.Int) (*big.Int, error) { + return _ERC1155.Contract.BalanceOf(&_ERC1155.CallOpts, account, id) +} + +// BalanceOfBatch is a free data retrieval call binding the contract method 0x4e1273f4. +// +// Solidity: function balanceOfBatch(address[] accounts, uint256[] ids) view returns(uint256[]) +func (_ERC1155 *ERC1155Caller) BalanceOfBatch(opts *bind.CallOpts, accounts []common.Address, ids []*big.Int) ([]*big.Int, error) { + var out []interface{} + err := _ERC1155.contract.Call(opts, &out, "balanceOfBatch", accounts, ids) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +// BalanceOfBatch is a free data retrieval call binding the contract method 0x4e1273f4. +// +// Solidity: function balanceOfBatch(address[] accounts, uint256[] ids) view returns(uint256[]) +func (_ERC1155 *ERC1155Session) BalanceOfBatch(accounts []common.Address, ids []*big.Int) ([]*big.Int, error) { + return _ERC1155.Contract.BalanceOfBatch(&_ERC1155.CallOpts, accounts, ids) +} + +// BalanceOfBatch is a free data retrieval call binding the contract method 0x4e1273f4. +// +// Solidity: function balanceOfBatch(address[] accounts, uint256[] ids) view returns(uint256[]) +func (_ERC1155 *ERC1155CallerSession) BalanceOfBatch(accounts []common.Address, ids []*big.Int) ([]*big.Int, error) { + return _ERC1155.Contract.BalanceOfBatch(&_ERC1155.CallOpts, accounts, ids) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address account, address operator) view returns(bool) +func (_ERC1155 *ERC1155Caller) IsApprovedForAll(opts *bind.CallOpts, account common.Address, operator common.Address) (bool, error) { + var out []interface{} + err := _ERC1155.contract.Call(opts, &out, "isApprovedForAll", account, operator) + + 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 account, address operator) view returns(bool) +func (_ERC1155 *ERC1155Session) IsApprovedForAll(account common.Address, operator common.Address) (bool, error) { + return _ERC1155.Contract.IsApprovedForAll(&_ERC1155.CallOpts, account, operator) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address account, address operator) view returns(bool) +func (_ERC1155 *ERC1155CallerSession) IsApprovedForAll(account common.Address, operator common.Address) (bool, error) { + return _ERC1155.Contract.IsApprovedForAll(&_ERC1155.CallOpts, account, operator) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC1155 *ERC1155Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC1155.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 (_ERC1155 *ERC1155Session) Name() (string, error) { + return _ERC1155.Contract.Name(&_ERC1155.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC1155 *ERC1155CallerSession) Name() (string, error) { + return _ERC1155.Contract.Name(&_ERC1155.CallOpts) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_ERC1155 *ERC1155Caller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _ERC1155.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) view returns(bool) +func (_ERC1155 *ERC1155Session) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _ERC1155.Contract.SupportsInterface(&_ERC1155.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_ERC1155 *ERC1155CallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _ERC1155.Contract.SupportsInterface(&_ERC1155.CallOpts, interfaceId) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC1155 *ERC1155Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC1155.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 (_ERC1155 *ERC1155Session) Symbol() (string, error) { + return _ERC1155.Contract.Symbol(&_ERC1155.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC1155 *ERC1155CallerSession) Symbol() (string, error) { + return _ERC1155.Contract.Symbol(&_ERC1155.CallOpts) +} + +// Uri is a free data retrieval call binding the contract method 0x0e89341c. +// +// Solidity: function uri(uint256 ) view returns(string) +func (_ERC1155 *ERC1155Caller) Uri(opts *bind.CallOpts, arg0 *big.Int) (string, error) { + var out []interface{} + err := _ERC1155.contract.Call(opts, &out, "uri", arg0) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Uri is a free data retrieval call binding the contract method 0x0e89341c. +// +// Solidity: function uri(uint256 ) view returns(string) +func (_ERC1155 *ERC1155Session) Uri(arg0 *big.Int) (string, error) { + return _ERC1155.Contract.Uri(&_ERC1155.CallOpts, arg0) +} + +// Uri is a free data retrieval call binding the contract method 0x0e89341c. +// +// Solidity: function uri(uint256 ) view returns(string) +func (_ERC1155 *ERC1155CallerSession) Uri(arg0 *big.Int) (string, error) { + return _ERC1155.Contract.Uri(&_ERC1155.CallOpts, arg0) +} + +// SafeBatchTransferFrom is a paid mutator transaction binding the contract method 0x2eb2c2d6. +// +// Solidity: function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) returns() +func (_ERC1155 *ERC1155Transactor) SafeBatchTransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, ids []*big.Int, amounts []*big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.contract.Transact(opts, "safeBatchTransferFrom", from, to, ids, amounts, data) +} + +// SafeBatchTransferFrom is a paid mutator transaction binding the contract method 0x2eb2c2d6. +// +// Solidity: function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) returns() +func (_ERC1155 *ERC1155Session) SafeBatchTransferFrom(from common.Address, to common.Address, ids []*big.Int, amounts []*big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.Contract.SafeBatchTransferFrom(&_ERC1155.TransactOpts, from, to, ids, amounts, data) +} + +// SafeBatchTransferFrom is a paid mutator transaction binding the contract method 0x2eb2c2d6. +// +// Solidity: function safeBatchTransferFrom(address from, address to, uint256[] ids, uint256[] amounts, bytes data) returns() +func (_ERC1155 *ERC1155TransactorSession) SafeBatchTransferFrom(from common.Address, to common.Address, ids []*big.Int, amounts []*big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.Contract.SafeBatchTransferFrom(&_ERC1155.TransactOpts, from, to, ids, amounts, data) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0xf242432a. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) returns() +func (_ERC1155 *ERC1155Transactor) SafeTransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, id *big.Int, amount *big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.contract.Transact(opts, "safeTransferFrom", from, to, id, amount, data) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0xf242432a. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) returns() +func (_ERC1155 *ERC1155Session) SafeTransferFrom(from common.Address, to common.Address, id *big.Int, amount *big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.Contract.SafeTransferFrom(&_ERC1155.TransactOpts, from, to, id, amount, data) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0xf242432a. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data) returns() +func (_ERC1155 *ERC1155TransactorSession) SafeTransferFrom(from common.Address, to common.Address, id *big.Int, amount *big.Int, data []byte) (*types.Transaction, error) { + return _ERC1155.Contract.SafeTransferFrom(&_ERC1155.TransactOpts, from, to, id, amount, data) +} + +// SetApprovalForAll is a paid mutator transaction binding the contract method 0xa22cb465. +// +// Solidity: function setApprovalForAll(address operator, bool approved) returns() +func (_ERC1155 *ERC1155Transactor) SetApprovalForAll(opts *bind.TransactOpts, operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC1155.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 (_ERC1155 *ERC1155Session) SetApprovalForAll(operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC1155.Contract.SetApprovalForAll(&_ERC1155.TransactOpts, operator, approved) +} + +// SetApprovalForAll is a paid mutator transaction binding the contract method 0xa22cb465. +// +// Solidity: function setApprovalForAll(address operator, bool approved) returns() +func (_ERC1155 *ERC1155TransactorSession) SetApprovalForAll(operator common.Address, approved bool) (*types.Transaction, error) { + return _ERC1155.Contract.SetApprovalForAll(&_ERC1155.TransactOpts, operator, approved) +} + +// ERC1155ApprovalForAllIterator is returned from FilterApprovalForAll and is used to iterate over the raw logs and unpacked data for ApprovalForAll events raised by the ERC1155 contract. +type ERC1155ApprovalForAllIterator struct { + Event *ERC1155ApprovalForAll // 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 *ERC1155ApprovalForAllIterator) 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(ERC1155ApprovalForAll) + 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(ERC1155ApprovalForAll) + 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 *ERC1155ApprovalForAllIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1155ApprovalForAllIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1155ApprovalForAll represents a ApprovalForAll event raised by the ERC1155 contract. +type ERC1155ApprovalForAll struct { + Account 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 account, address indexed operator, bool approved) +func (_ERC1155 *ERC1155Filterer) FilterApprovalForAll(opts *bind.FilterOpts, account []common.Address, operator []common.Address) (*ERC1155ApprovalForAllIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + + logs, sub, err := _ERC1155.contract.FilterLogs(opts, "ApprovalForAll", accountRule, operatorRule) + if err != nil { + return nil, err + } + return &ERC1155ApprovalForAllIterator{contract: _ERC1155.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 account, address indexed operator, bool approved) +func (_ERC1155 *ERC1155Filterer) WatchApprovalForAll(opts *bind.WatchOpts, sink chan<- *ERC1155ApprovalForAll, account []common.Address, operator []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + + logs, sub, err := _ERC1155.contract.WatchLogs(opts, "ApprovalForAll", accountRule, 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(ERC1155ApprovalForAll) + if err := _ERC1155.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 account, address indexed operator, bool approved) +func (_ERC1155 *ERC1155Filterer) ParseApprovalForAll(log types.Log) (*ERC1155ApprovalForAll, error) { + event := new(ERC1155ApprovalForAll) + if err := _ERC1155.contract.UnpackLog(event, "ApprovalForAll", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC1155TransferBatchIterator is returned from FilterTransferBatch and is used to iterate over the raw logs and unpacked data for TransferBatch events raised by the ERC1155 contract. +type ERC1155TransferBatchIterator struct { + Event *ERC1155TransferBatch // 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 *ERC1155TransferBatchIterator) 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(ERC1155TransferBatch) + 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(ERC1155TransferBatch) + 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 *ERC1155TransferBatchIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1155TransferBatchIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1155TransferBatch represents a TransferBatch event raised by the ERC1155 contract. +type ERC1155TransferBatch struct { + Operator common.Address + From common.Address + To common.Address + Ids []*big.Int + Values []*big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransferBatch is a free log retrieval operation binding the contract event 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb. +// +// Solidity: event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values) +func (_ERC1155 *ERC1155Filterer) FilterTransferBatch(opts *bind.FilterOpts, operator []common.Address, from []common.Address, to []common.Address) (*ERC1155TransferBatchIterator, error) { + + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + 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 := _ERC1155.contract.FilterLogs(opts, "TransferBatch", operatorRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &ERC1155TransferBatchIterator{contract: _ERC1155.contract, event: "TransferBatch", logs: logs, sub: sub}, nil +} + +// WatchTransferBatch is a free log subscription operation binding the contract event 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb. +// +// Solidity: event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values) +func (_ERC1155 *ERC1155Filterer) WatchTransferBatch(opts *bind.WatchOpts, sink chan<- *ERC1155TransferBatch, operator []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + 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 := _ERC1155.contract.WatchLogs(opts, "TransferBatch", operatorRule, 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(ERC1155TransferBatch) + if err := _ERC1155.contract.UnpackLog(event, "TransferBatch", 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 +} + +// ParseTransferBatch is a log parse operation binding the contract event 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb. +// +// Solidity: event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values) +func (_ERC1155 *ERC1155Filterer) ParseTransferBatch(log types.Log) (*ERC1155TransferBatch, error) { + event := new(ERC1155TransferBatch) + if err := _ERC1155.contract.UnpackLog(event, "TransferBatch", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC1155TransferSingleIterator is returned from FilterTransferSingle and is used to iterate over the raw logs and unpacked data for TransferSingle events raised by the ERC1155 contract. +type ERC1155TransferSingleIterator struct { + Event *ERC1155TransferSingle // 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 *ERC1155TransferSingleIterator) 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(ERC1155TransferSingle) + 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(ERC1155TransferSingle) + 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 *ERC1155TransferSingleIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1155TransferSingleIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1155TransferSingle represents a TransferSingle event raised by the ERC1155 contract. +type ERC1155TransferSingle struct { + Operator common.Address + From common.Address + To common.Address + Id *big.Int + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransferSingle is a free log retrieval operation binding the contract event 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62. +// +// Solidity: event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value) +func (_ERC1155 *ERC1155Filterer) FilterTransferSingle(opts *bind.FilterOpts, operator []common.Address, from []common.Address, to []common.Address) (*ERC1155TransferSingleIterator, error) { + + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + 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 := _ERC1155.contract.FilterLogs(opts, "TransferSingle", operatorRule, fromRule, toRule) + if err != nil { + return nil, err + } + return &ERC1155TransferSingleIterator{contract: _ERC1155.contract, event: "TransferSingle", logs: logs, sub: sub}, nil +} + +// WatchTransferSingle is a free log subscription operation binding the contract event 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62. +// +// Solidity: event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value) +func (_ERC1155 *ERC1155Filterer) WatchTransferSingle(opts *bind.WatchOpts, sink chan<- *ERC1155TransferSingle, operator []common.Address, from []common.Address, to []common.Address) (event.Subscription, error) { + + var operatorRule []interface{} + for _, operatorItem := range operator { + operatorRule = append(operatorRule, operatorItem) + } + 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 := _ERC1155.contract.WatchLogs(opts, "TransferSingle", operatorRule, 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(ERC1155TransferSingle) + if err := _ERC1155.contract.UnpackLog(event, "TransferSingle", 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 +} + +// ParseTransferSingle is a log parse operation binding the contract event 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62. +// +// Solidity: event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value) +func (_ERC1155 *ERC1155Filterer) ParseTransferSingle(log types.Log) (*ERC1155TransferSingle, error) { + event := new(ERC1155TransferSingle) + if err := _ERC1155.contract.UnpackLog(event, "TransferSingle", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC1155URIIterator is returned from FilterURI and is used to iterate over the raw logs and unpacked data for URI events raised by the ERC1155 contract. +type ERC1155URIIterator struct { + Event *ERC1155URI // 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 *ERC1155URIIterator) 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(ERC1155URI) + 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(ERC1155URI) + 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 *ERC1155URIIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1155URIIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1155URI represents a URI event raised by the ERC1155 contract. +type ERC1155URI struct { + Value string + Id *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterURI is a free log retrieval operation binding the contract event 0x6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b. +// +// Solidity: event URI(string value, uint256 indexed id) +func (_ERC1155 *ERC1155Filterer) FilterURI(opts *bind.FilterOpts, id []*big.Int) (*ERC1155URIIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC1155.contract.FilterLogs(opts, "URI", idRule) + if err != nil { + return nil, err + } + return &ERC1155URIIterator{contract: _ERC1155.contract, event: "URI", logs: logs, sub: sub}, nil +} + +// WatchURI is a free log subscription operation binding the contract event 0x6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b. +// +// Solidity: event URI(string value, uint256 indexed id) +func (_ERC1155 *ERC1155Filterer) WatchURI(opts *bind.WatchOpts, sink chan<- *ERC1155URI, id []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _ERC1155.contract.WatchLogs(opts, "URI", 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(ERC1155URI) + if err := _ERC1155.contract.UnpackLog(event, "URI", 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 +} + +// ParseURI is a log parse operation binding the contract event 0x6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b. +// +// Solidity: event URI(string value, uint256 indexed id) +func (_ERC1155 *ERC1155Filterer) ParseURI(log types.Log) (*ERC1155URI, error) { + event := new(ERC1155URI) + if err := _ERC1155.contract.UnpackLog(event, "URI", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/provider/ethereum/contract/erc165/abi/ERC165.abi b/provider/ethereum/contract/erc165/abi/ERC165.abi new file mode 100644 index 00000000..0777c4df --- /dev/null +++ b/provider/ethereum/contract/erc165/abi/ERC165.abi @@ -0,0 +1,21 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/provider/ethereum/contract/erc165/contract.go b/provider/ethereum/contract/erc165/contract.go new file mode 100644 index 00000000..fe458ff4 --- /dev/null +++ b/provider/ethereum/contract/erc165/contract.go @@ -0,0 +1,4 @@ +package erc165 + +// https://eips.ethereum.org/EIPS/eip-165 +//go:generate go run --mod=mod github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 --abi ./abi/ERC165.abi --pkg erc165 --type ERC165 --out contract_erc165.go diff --git a/provider/ethereum/contract/erc165/contract_erc165.go b/provider/ethereum/contract/erc165/contract_erc165.go new file mode 100644 index 00000000..ba489435 --- /dev/null +++ b/provider/ethereum/contract/erc165/contract_erc165.go @@ -0,0 +1,212 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc165 + +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 +) + +// ERC165MetaData contains all meta data concerning the ERC165 contract. +var ERC165MetaData = &bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[{\"name\":\"interfaceID\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// ERC165ABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC165MetaData.ABI instead. +var ERC165ABI = ERC165MetaData.ABI + +// ERC165 is an auto generated Go binding around an Ethereum contract. +type ERC165 struct { + ERC165Caller // Read-only binding to the contract + ERC165Transactor // Write-only binding to the contract + ERC165Filterer // Log filterer for contract events +} + +// ERC165Caller is an auto generated read-only Go binding around an Ethereum contract. +type ERC165Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC165Transactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC165Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC165Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC165Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC165Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC165Session struct { + Contract *ERC165 // 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 +} + +// ERC165CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC165CallerSession struct { + Contract *ERC165Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC165TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC165TransactorSession struct { + Contract *ERC165Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC165Raw is an auto generated low-level Go binding around an Ethereum contract. +type ERC165Raw struct { + Contract *ERC165 // Generic contract binding to access the raw methods on +} + +// ERC165CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC165CallerRaw struct { + Contract *ERC165Caller // Generic read-only contract binding to access the raw methods on +} + +// ERC165TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC165TransactorRaw struct { + Contract *ERC165Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC165 creates a new instance of ERC165, bound to a specific deployed contract. +func NewERC165(address common.Address, backend bind.ContractBackend) (*ERC165, error) { + contract, err := bindERC165(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC165{ERC165Caller: ERC165Caller{contract: contract}, ERC165Transactor: ERC165Transactor{contract: contract}, ERC165Filterer: ERC165Filterer{contract: contract}}, nil +} + +// NewERC165Caller creates a new read-only instance of ERC165, bound to a specific deployed contract. +func NewERC165Caller(address common.Address, caller bind.ContractCaller) (*ERC165Caller, error) { + contract, err := bindERC165(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC165Caller{contract: contract}, nil +} + +// NewERC165Transactor creates a new write-only instance of ERC165, bound to a specific deployed contract. +func NewERC165Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC165Transactor, error) { + contract, err := bindERC165(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC165Transactor{contract: contract}, nil +} + +// NewERC165Filterer creates a new log filterer instance of ERC165, bound to a specific deployed contract. +func NewERC165Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC165Filterer, error) { + contract, err := bindERC165(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC165Filterer{contract: contract}, nil +} + +// bindERC165 binds a generic wrapper to an already deployed contract. +func bindERC165(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC165MetaData.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 (_ERC165 *ERC165Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC165.Contract.ERC165Caller.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 (_ERC165 *ERC165Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC165.Contract.ERC165Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC165 *ERC165Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC165.Contract.ERC165Transactor.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 (_ERC165 *ERC165CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC165.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 (_ERC165 *ERC165TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC165.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC165 *ERC165TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC165.Contract.contract.Transact(opts, method, params...) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceID) view returns(bool) +func (_ERC165 *ERC165Caller) SupportsInterface(opts *bind.CallOpts, interfaceID [4]byte) (bool, error) { + var out []interface{} + err := _ERC165.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) view returns(bool) +func (_ERC165 *ERC165Session) SupportsInterface(interfaceID [4]byte) (bool, error) { + return _ERC165.Contract.SupportsInterface(&_ERC165.CallOpts, interfaceID) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceID) view returns(bool) +func (_ERC165 *ERC165CallerSession) SupportsInterface(interfaceID [4]byte) (bool, error) { + return _ERC165.Contract.SupportsInterface(&_ERC165.CallOpts, interfaceID) +} diff --git a/provider/ethereum/contract/erc20/abi/ERC20.abi b/provider/ethereum/contract/erc20/abi/ERC20.abi new file mode 100644 index 00000000..e1f61ef8 --- /dev/null +++ b/provider/ethereum/contract/erc20/abi/ERC20.abi @@ -0,0 +1,288 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "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": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "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": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/provider/ethereum/contract/erc20/contract.go b/provider/ethereum/contract/erc20/contract.go new file mode 100644 index 00000000..87dfaae9 --- /dev/null +++ b/provider/ethereum/contract/erc20/contract.go @@ -0,0 +1,15 @@ +package erc20 + +import "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + +// https://eips.ethereum.org/EIPS/eip-20 +//go:generate go run -mod=mod github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 --abi ./abi/ERC20.abi --pkg erc20 --type ERC20 --out contract_erc20.go + +var ( + MethodIDTransfer = contract.MethodID("transfer(address,uint256)") + MethodIDTransferFrom = contract.MethodID("transferFrom(address,address,uint256)") + MethodIDApprove = contract.MethodID("approve(address,uint256)") + + EventHashTransfer = contract.EventHash("Transfer(address,address,uint256)") + EventHashApproval = contract.EventHash("Approval(address,address,uint256)") +) diff --git a/provider/ethereum/contract/erc20/contract_erc20.go b/provider/ethereum/contract/erc20/contract_erc20.go new file mode 100644 index 00000000..24012987 --- /dev/null +++ b/provider/ethereum/contract/erc20/contract_erc20.go @@ -0,0 +1,780 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc20 + +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: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name_\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"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\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// ERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC20MetaData.ABI instead. +var ERC20ABI = ERC20MetaData.ABI + +// 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 owner, address spender) view returns(uint256) +func (_ERC20 *ERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "allowance", owner, spender) + + 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 owner, address spender) view returns(uint256) +func (_ERC20 *ERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _ERC20.Contract.Allowance(&_ERC20.CallOpts, owner, spender) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_ERC20 *ERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _ERC20.Contract.Allowance(&_ERC20.CallOpts, owner, spender) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_ERC20 *ERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20.contract.Call(opts, &out, "balanceOf", account) + + 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 account) view returns(uint256) +func (_ERC20 *ERC20Session) BalanceOf(account common.Address) (*big.Int, error) { + return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, account) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_ERC20 *ERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, account) +} + +// 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) +} + +// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. +// +// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) +func (_ERC20 *ERC20Transactor) DecreaseAllowance(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "decreaseAllowance", spender, subtractedValue) +} + +// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. +// +// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) +func (_ERC20 *ERC20Session) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.DecreaseAllowance(&_ERC20.TransactOpts, spender, subtractedValue) +} + +// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. +// +// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) +func (_ERC20 *ERC20TransactorSession) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.DecreaseAllowance(&_ERC20.TransactOpts, spender, subtractedValue) +} + +// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. +// +// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) +func (_ERC20 *ERC20Transactor) IncreaseAllowance(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "increaseAllowance", spender, addedValue) +} + +// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. +// +// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) +func (_ERC20 *ERC20Session) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.IncreaseAllowance(&_ERC20.TransactOpts, spender, addedValue) +} + +// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. +// +// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) +func (_ERC20 *ERC20TransactorSession) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.IncreaseAllowance(&_ERC20.TransactOpts, spender, addedValue) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "transfer", to, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20Session) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, to, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20TransactorSession) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, to, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.contract.Transact(opts, "transferFrom", from, to, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20Session) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, from, to, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 amount) returns(bool) +func (_ERC20 *ERC20TransactorSession) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, from, to, 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/provider/ethereum/contract/erc721/abi/ERC721.abi b/provider/ethereum/contract/erc721/abi/ERC721.abi new file mode 100644 index 00000000..a17563bc --- /dev/null +++ b/provider/ethereum/contract/erc721/abi/ERC721.abi @@ -0,0 +1,348 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "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": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "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": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "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": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "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": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "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" + } +] diff --git a/provider/ethereum/contract/erc721/contract.go b/provider/ethereum/contract/erc721/contract.go new file mode 100644 index 00000000..f4885cd9 --- /dev/null +++ b/provider/ethereum/contract/erc721/contract.go @@ -0,0 +1,17 @@ +package erc721 + +import "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + +// https://eips.ethereum.org/EIPS/eip-721 +//go:generate go run --mod=mod github.com/ethereum/go-ethereum/cmd/abigen@v1.13.5 --abi ./abi/ERC721.abi --pkg erc721 --type ERC721 --out contract_erc721.go + +var ( + MethodIDSafeTransferFrom = contract.MethodID("safeTransferFrom(address,address,uint256)") + MethodIDSafeTransferFromAndCallReceiver = contract.MethodID("safeTransferFrom(address,address,uint256,bytes)") + MethodIDApprove = contract.MethodID("approve(address,uint256)") + MethodIDSetApprovalForAll = contract.MethodID("setApprovalForAll(address,bool)") + + EventHashTransfer = contract.EventHash("Transfer(address,address,uint256)") + EventHashApproval = contract.EventHash("Approval(address,address,uint256)") + EventHashApprovalForAll = contract.EventHash("ApprovalForAll(address,address,bool)") +) diff --git a/provider/ethereum/contract/erc721/contract_erc721.go b/provider/ethereum/contract/erc721/contract_erc721.go new file mode 100644 index 00000000..17f58cf9 --- /dev/null +++ b/provider/ethereum/contract/erc721/contract_erc721.go @@ -0,0 +1,1012 @@ +// 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: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name_\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"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\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"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\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"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\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"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\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\"}]", +} + +// 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 tokenId) view returns(address) +func (_ERC721 *ERC721Caller) GetApproved(opts *bind.CallOpts, tokenId *big.Int) (common.Address, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "getApproved", tokenId) + + 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 tokenId) view returns(address) +func (_ERC721 *ERC721Session) GetApproved(tokenId *big.Int) (common.Address, error) { + return _ERC721.Contract.GetApproved(&_ERC721.CallOpts, tokenId) +} + +// GetApproved is a free data retrieval call binding the contract method 0x081812fc. +// +// Solidity: function getApproved(uint256 tokenId) view returns(address) +func (_ERC721 *ERC721CallerSession) GetApproved(tokenId *big.Int) (common.Address, error) { + return _ERC721.Contract.GetApproved(&_ERC721.CallOpts, tokenId) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address owner, address operator) view returns(bool) +func (_ERC721 *ERC721Caller) IsApprovedForAll(opts *bind.CallOpts, owner common.Address, operator common.Address) (bool, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "isApprovedForAll", owner, operator) + + 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 owner, address operator) view returns(bool) +func (_ERC721 *ERC721Session) IsApprovedForAll(owner common.Address, operator common.Address) (bool, error) { + return _ERC721.Contract.IsApprovedForAll(&_ERC721.CallOpts, owner, operator) +} + +// IsApprovedForAll is a free data retrieval call binding the contract method 0xe985e9c5. +// +// Solidity: function isApprovedForAll(address owner, address operator) view returns(bool) +func (_ERC721 *ERC721CallerSession) IsApprovedForAll(owner common.Address, operator common.Address) (bool, error) { + return _ERC721.Contract.IsApprovedForAll(&_ERC721.CallOpts, owner, operator) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC721 *ERC721Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC721.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 (_ERC721 *ERC721Session) Name() (string, error) { + return _ERC721.Contract.Name(&_ERC721.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC721 *ERC721CallerSession) Name() (string, error) { + return _ERC721.Contract.Name(&_ERC721.CallOpts) +} + +// OwnerOf is a free data retrieval call binding the contract method 0x6352211e. +// +// Solidity: function ownerOf(uint256 tokenId) view returns(address) +func (_ERC721 *ERC721Caller) OwnerOf(opts *bind.CallOpts, tokenId *big.Int) (common.Address, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "ownerOf", tokenId) + + 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 tokenId) view returns(address) +func (_ERC721 *ERC721Session) OwnerOf(tokenId *big.Int) (common.Address, error) { + return _ERC721.Contract.OwnerOf(&_ERC721.CallOpts, tokenId) +} + +// OwnerOf is a free data retrieval call binding the contract method 0x6352211e. +// +// Solidity: function ownerOf(uint256 tokenId) view returns(address) +func (_ERC721 *ERC721CallerSession) OwnerOf(tokenId *big.Int) (common.Address, error) { + return _ERC721.Contract.OwnerOf(&_ERC721.CallOpts, tokenId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view 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) view 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) view returns(bool) +func (_ERC721 *ERC721CallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _ERC721.Contract.SupportsInterface(&_ERC721.CallOpts, interfaceId) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC721 *ERC721Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ERC721.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 (_ERC721 *ERC721Session) Symbol() (string, error) { + return _ERC721.Contract.Symbol(&_ERC721.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC721 *ERC721CallerSession) Symbol() (string, error) { + return _ERC721.Contract.Symbol(&_ERC721.CallOpts) +} + +// TokenURI is a free data retrieval call binding the contract method 0xc87b56dd. +// +// Solidity: function tokenURI(uint256 tokenId) view returns(string) +func (_ERC721 *ERC721Caller) TokenURI(opts *bind.CallOpts, tokenId *big.Int) (string, error) { + var out []interface{} + err := _ERC721.contract.Call(opts, &out, "tokenURI", tokenId) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// TokenURI is a free data retrieval call binding the contract method 0xc87b56dd. +// +// Solidity: function tokenURI(uint256 tokenId) view returns(string) +func (_ERC721 *ERC721Session) TokenURI(tokenId *big.Int) (string, error) { + return _ERC721.Contract.TokenURI(&_ERC721.CallOpts, tokenId) +} + +// TokenURI is a free data retrieval call binding the contract method 0xc87b56dd. +// +// Solidity: function tokenURI(uint256 tokenId) view returns(string) +func (_ERC721 *ERC721CallerSession) TokenURI(tokenId *big.Int) (string, error) { + return _ERC721.Contract.TokenURI(&_ERC721.CallOpts, tokenId) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address to, uint256 tokenId) returns() +func (_ERC721 *ERC721Transactor) Approve(opts *bind.TransactOpts, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "approve", to, tokenId) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address to, uint256 tokenId) returns() +func (_ERC721 *ERC721Session) Approve(to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Approve(&_ERC721.TransactOpts, to, tokenId) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address to, uint256 tokenId) returns() +func (_ERC721 *ERC721TransactorSession) Approve(to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.Approve(&_ERC721.TransactOpts, to, tokenId) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId) returns() +func (_ERC721 *ERC721Transactor) SafeTransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "safeTransferFrom", from, to, tokenId) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId) returns() +func (_ERC721 *ERC721Session) SafeTransferFrom(from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom(&_ERC721.TransactOpts, from, to, tokenId) +} + +// SafeTransferFrom is a paid mutator transaction binding the contract method 0x42842e0e. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId) returns() +func (_ERC721 *ERC721TransactorSession) SafeTransferFrom(from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom(&_ERC721.TransactOpts, from, to, tokenId) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) returns() +func (_ERC721 *ERC721Transactor) SafeTransferFrom0(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "safeTransferFrom0", from, to, tokenId, data) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) returns() +func (_ERC721 *ERC721Session) SafeTransferFrom0(from common.Address, to common.Address, tokenId *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom0(&_ERC721.TransactOpts, from, to, tokenId, data) +} + +// SafeTransferFrom0 is a paid mutator transaction binding the contract method 0xb88d4fde. +// +// Solidity: function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) returns() +func (_ERC721 *ERC721TransactorSession) SafeTransferFrom0(from common.Address, to common.Address, tokenId *big.Int, data []byte) (*types.Transaction, error) { + return _ERC721.Contract.SafeTransferFrom0(&_ERC721.TransactOpts, from, to, tokenId, 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 tokenId) returns() +func (_ERC721 *ERC721Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.contract.Transact(opts, "transferFrom", from, to, tokenId) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 tokenId) returns() +func (_ERC721 *ERC721Session) TransferFrom(from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.TransferFrom(&_ERC721.TransactOpts, from, to, tokenId) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 tokenId) returns() +func (_ERC721 *ERC721TransactorSession) TransferFrom(from common.Address, to common.Address, tokenId *big.Int) (*types.Transaction, error) { + return _ERC721.Contract.TransferFrom(&_ERC721.TransactOpts, from, to, tokenId) +} + +// 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 + Approved common.Address + TokenId *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 approved, uint256 indexed tokenId) +func (_ERC721 *ERC721Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, approved []common.Address, tokenId []*big.Int) (*ERC721ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var approvedRule []interface{} + for _, approvedItem := range approved { + approvedRule = append(approvedRule, approvedItem) + } + var tokenIdRule []interface{} + for _, tokenIdItem := range tokenId { + tokenIdRule = append(tokenIdRule, tokenIdItem) + } + + logs, sub, err := _ERC721.contract.FilterLogs(opts, "Approval", ownerRule, approvedRule, tokenIdRule) + 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 approved, uint256 indexed tokenId) +func (_ERC721 *ERC721Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *ERC721Approval, owner []common.Address, approved []common.Address, tokenId []*big.Int) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var approvedRule []interface{} + for _, approvedItem := range approved { + approvedRule = append(approvedRule, approvedItem) + } + var tokenIdRule []interface{} + for _, tokenIdItem := range tokenId { + tokenIdRule = append(tokenIdRule, tokenIdItem) + } + + logs, sub, err := _ERC721.contract.WatchLogs(opts, "Approval", ownerRule, approvedRule, tokenIdRule) + 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 approved, uint256 indexed tokenId) +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 + TokenId *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 tokenId) +func (_ERC721 *ERC721Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address, tokenId []*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 tokenIdRule []interface{} + for _, tokenIdItem := range tokenId { + tokenIdRule = append(tokenIdRule, tokenIdItem) + } + + logs, sub, err := _ERC721.contract.FilterLogs(opts, "Transfer", fromRule, toRule, tokenIdRule) + 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 tokenId) +func (_ERC721 *ERC721Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ERC721Transfer, from []common.Address, to []common.Address, tokenId []*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 tokenIdRule []interface{} + for _, tokenIdItem := range tokenId { + tokenIdRule = append(tokenIdRule, tokenIdItem) + } + + logs, sub, err := _ERC721.contract.WatchLogs(opts, "Transfer", fromRule, toRule, tokenIdRule) + 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 tokenId) +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/provider/ethereum/contract/selector.go b/provider/ethereum/contract/selector.go new file mode 100644 index 00000000..b70eb457 --- /dev/null +++ b/provider/ethereum/contract/selector.go @@ -0,0 +1,69 @@ +package contract + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// MethodID calculates the method ID of the function selector. +func MethodID(method string) [4]byte { + var ( + methodID [4]byte + hash = crypto.Keccak256Hash([]byte(method)) + ) + + copy(methodID[:], hash[:4]) + + return methodID +} + +// EventHash calculates the event hash of the event signature. +func EventHash(event string) common.Hash { + return crypto.Keccak256Hash([]byte(event)) +} + +// MatchMethodIDs checks if the input matches any of the method IDs. +func MatchMethodIDs(input []byte, methodIDs ...[4]byte) bool { + for _, methodID := range methodIDs { + if bytes.HasPrefix(input, methodID[:]) { + return true + } + } + + return false +} + +// MatchEventHashes checks if the hash matches any of the event hashes. +func MatchEventHashes(hash common.Hash, eventHashes ...common.Hash) bool { + for _, eventHash := range eventHashes { + if hash == eventHash { + return true + } + } + + return false +} + +// MatchAddresses checks if the address matches any of the addresses. +func MatchAddresses(address common.Address, addresses ...common.Address) bool { + for _, addr := range addresses { + if address == addr { + return true + } + } + + return false +} + +// ContainsMethodIDs checks if the code contains all the method IDs. +func ContainsMethodIDs(code []byte, methodIDs ...[4]byte) bool { + for _, methodID := range methodIDs { + if exists := bytes.Contains(code, methodID[:]); !exists { + return exists + } + } + + return true +} diff --git a/provider/ethereum/contract/selector_test.go b/provider/ethereum/contract/selector_test.go new file mode 100644 index 00000000..d9a8742e --- /dev/null +++ b/provider/ethereum/contract/selector_test.go @@ -0,0 +1,60 @@ +package contract_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/stretchr/testify/require" +) + +func TestMethodID(t *testing.T) { + t.Parallel() + + type arguments struct { + value string + } + + testcases := []struct { + name string + arguments arguments + want require.ValueAssertionFunc + wantError require.ErrorAssertionFunc + }{ + { + name: "implementation()", + arguments: arguments{ + value: "implementation()", + }, + want: func(t require.TestingT, value interface{}, msgAndArgs ...interface{}) { + methodID, ok := value.(string) + require.True(t, ok) + + require.Equal(t, "0x5c60da1b", methodID) + }, + }, + { + name: "name()", + arguments: arguments{ + value: "name()", + }, + want: func(t require.TestingT, value interface{}, msgAndArgs ...interface{}) { + methodID, ok := value.(string) + require.True(t, ok) + + require.Equal(t, "0x06fdde03", methodID) + }, + }, + } + + for _, testcase := range testcases { + testcase := testcase + + t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + + result := contract.MethodID(testcase.arguments.value) + testcase.want(t, hexutil.Encode(result[:])) + }) + } +} diff --git a/provider/ethereum/contract/standard.go b/provider/ethereum/contract/standard.go new file mode 100644 index 00000000..b96fa3f6 --- /dev/null +++ b/provider/ethereum/contract/standard.go @@ -0,0 +1,13 @@ +package contract + +//go:generate go run --mod=mod github.com/dmarkham/enumer@v1.5.9 --values --type=Standard --output standard_string.go --linecomment --json --sql +type Standard int + +const ( + StandardUnknown Standard = 0 // Unknown + StandardERC20 Standard = 20 // ERC-20 + StandardERC165 Standard = 165 // ERC-165 + StandardERC721 Standard = 721 // ERC-721 + StandardERC1155 Standard = 1155 // ERC-1155 + StandardERC1967 Standard = 1967 // ERC-1967 +) diff --git a/provider/ethereum/contract/standard_string.go b/provider/ethereum/contract/standard_string.go new file mode 100644 index 00000000..8da5fea4 --- /dev/null +++ b/provider/ethereum/contract/standard_string.go @@ -0,0 +1,177 @@ +// Code generated by "enumer --values --type=Standard --output standard_string.go --linecomment --json --sql"; DO NOT EDIT. + +package contract + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "strings" +) + +const ( + _StandardName_0 = "Unknown" + _StandardLowerName_0 = "unknown" + _StandardName_1 = "ERC-20" + _StandardLowerName_1 = "erc-20" + _StandardName_2 = "ERC-165" + _StandardLowerName_2 = "erc-165" + _StandardName_3 = "ERC-721" + _StandardLowerName_3 = "erc-721" + _StandardName_4 = "ERC-1155" + _StandardLowerName_4 = "erc-1155" + _StandardName_5 = "ERC-1967" + _StandardLowerName_5 = "erc-1967" +) + +var ( + _StandardIndex_0 = [...]uint8{0, 7} + _StandardIndex_1 = [...]uint8{0, 6} + _StandardIndex_2 = [...]uint8{0, 7} + _StandardIndex_3 = [...]uint8{0, 7} + _StandardIndex_4 = [...]uint8{0, 8} + _StandardIndex_5 = [...]uint8{0, 8} +) + +func (i Standard) String() string { + switch { + case i == 0: + return _StandardName_0 + case i == 20: + return _StandardName_1 + case i == 165: + return _StandardName_2 + case i == 721: + return _StandardName_3 + case i == 1155: + return _StandardName_4 + case i == 1967: + return _StandardName_5 + default: + return fmt.Sprintf("Standard(%d)", i) + } +} + +func (Standard) Values() []string { + return StandardStrings() +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _StandardNoOp() { + var x [1]struct{} + _ = x[StandardUnknown-(0)] + _ = x[StandardERC20-(20)] + _ = x[StandardERC165-(165)] + _ = x[StandardERC721-(721)] + _ = x[StandardERC1155-(1155)] + _ = x[StandardERC1967-(1967)] +} + +var _StandardValues = []Standard{StandardUnknown, StandardERC20, StandardERC165, StandardERC721, StandardERC1155, StandardERC1967} + +var _StandardNameToValueMap = map[string]Standard{ + _StandardName_0[0:7]: StandardUnknown, + _StandardLowerName_0[0:7]: StandardUnknown, + _StandardName_1[0:6]: StandardERC20, + _StandardLowerName_1[0:6]: StandardERC20, + _StandardName_2[0:7]: StandardERC165, + _StandardLowerName_2[0:7]: StandardERC165, + _StandardName_3[0:7]: StandardERC721, + _StandardLowerName_3[0:7]: StandardERC721, + _StandardName_4[0:8]: StandardERC1155, + _StandardLowerName_4[0:8]: StandardERC1155, + _StandardName_5[0:8]: StandardERC1967, + _StandardLowerName_5[0:8]: StandardERC1967, +} + +var _StandardNames = []string{ + _StandardName_0[0:7], + _StandardName_1[0:6], + _StandardName_2[0:7], + _StandardName_3[0:7], + _StandardName_4[0:8], + _StandardName_5[0:8], +} + +// StandardString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func StandardString(s string) (Standard, error) { + if val, ok := _StandardNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _StandardNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to Standard values", s) +} + +// StandardValues returns all values of the enum +func StandardValues() []Standard { + return _StandardValues +} + +// StandardStrings returns a slice of all String values of the enum +func StandardStrings() []string { + strs := make([]string, len(_StandardNames)) + copy(strs, _StandardNames) + return strs +} + +// IsAStandard returns "true" if the value is listed in the enum definition. "false" otherwise +func (i Standard) IsAStandard() bool { + for _, v := range _StandardValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for Standard +func (i Standard) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for Standard +func (i *Standard) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("Standard should be a string, got %s", data) + } + + var err error + *i, err = StandardString(s) + return err +} + +func (i Standard) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *Standard) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of Standard: %[1]T(%[1]v)", value) + } + + val, err := StandardString(str) + if err != nil { + return err + } + + *i = val + return nil +} diff --git a/provider/ethereum/proxy/proxy.go b/provider/ethereum/proxy/proxy.go new file mode 100644 index 00000000..06abbfbc --- /dev/null +++ b/provider/ethereum/proxy/proxy.go @@ -0,0 +1,63 @@ +package proxy + +import ( + "context" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/naturalselectionlabs/rss3-node/common/sync" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" +) + +var ( + // SlotERC1967 key from https://eips.ethereum.org/EIPS/eip-1967#:~:text=0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc + SlotERC1967 = common.HexToHash("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc") + // SlotOpenZeppelin key from https://github.com/OpenZeppelin/openzeppelin-labs/blob/54ad91472fdd0ac4c34aa97d3a3da45c28245510/initializer_with_sol_editing/contracts/UpgradeabilityProxy.sol#L24 + SlotOpenZeppelin = common.HexToHash("0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3") // https://github.com/OpenZeppelin/openzeppelin-labs/blob/54ad91472fdd0ac4c34aa97d3a3da45c28245510/initializer_with_sol_editing/contracts/UpgradeabilityProxy.sol#L19-L24 + + slots = []common.Hash{ + SlotERC1967, + SlotOpenZeppelin, + } +) + +var ErrorNotAProxyContract = errors.New("not a proxy contract") + +// GetImplementation returns the implementation address of the proxy contract, +// it used sync.QuickGroup to get the result as soon as possible. +func GetImplementation(ctx context.Context, address common.Address, blockNumber *big.Int, ethereumClient ethereum.Client) (*common.Address, error) { + quickGroup := sync.NewQuickGroup[*common.Address](ctx) + + for _, slot := range slots { + slot := slot + + quickGroup.Go(func(ctx context.Context) (*common.Address, error) { + data, err := ethereumClient.StorageAt(ctx, address, slot, blockNumber) + if err != nil { + return nil, fmt.Errorf("get storage at slot %s: %w", slot.String(), err) + } + + result := common.BytesToAddress(data) + + if result == ethereum.AddressGenesis { + return nil, fmt.Errorf("invalid result %s", common.Bytes2Hex(data)) + } + + return &result, nil + }) + } + + result, err := quickGroup.Wait() + if err != nil { + // If all slots is empty, then it's not a proxy contract. + if errors.Is(err, sync.ErrorNoResult) { + return nil, ErrorNotAProxyContract + } + + return nil, err + } + + return result, nil +} diff --git a/provider/ethereum/proxy/proxy_test.go b/provider/ethereum/proxy/proxy_test.go new file mode 100644 index 00000000..658dc9d1 --- /dev/null +++ b/provider/ethereum/proxy/proxy_test.go @@ -0,0 +1,64 @@ +package proxy_test + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/endpoint" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/proxy" + "github.com/naturalselectionlabs/rss3-node/schema/filter" + "github.com/stretchr/testify/require" +) + +func TestGetImplementation(t *testing.T) { + t.Parallel() + + type arguments struct { + ctx context.Context + chain filter.ChainEthereum + address common.Address + blockNumber *big.Int + } + + testcases := []struct { + name string + arguments arguments + want require.ValueAssertionFunc + wantError require.ErrorAssertionFunc + }{ + { + name: "Ethereum USD Coin", + arguments: arguments{ + ctx: context.Background(), + chain: filter.ChainEthereumMainnet, + address: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), + }, + want: func(t require.TestingT, value interface{}, msgAndArgs ...interface{}) { + address, ok := value.(*common.Address) + require.True(t, ok) + + require.Equal(t, common.HexToAddress("0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF"), *address) + }, + wantError: require.NoError, + }, + } + + for _, testcase := range testcases { + testcase := testcase + + t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + + ethereumClient, err := ethereum.Dial(testcase.arguments.ctx, endpoint.MustGet(testcase.arguments.chain)) + testcase.wantError(t, err) + + result, err := proxy.GetImplementation(testcase.arguments.ctx, testcase.arguments.address, testcase.arguments.blockNumber, ethereumClient) + testcase.wantError(t, err) + + testcase.want(t, result) + }) + } +} diff --git a/provider/ethereum/type.go b/provider/ethereum/type.go new file mode 100644 index 00000000..0f09136c --- /dev/null +++ b/provider/ethereum/type.go @@ -0,0 +1,197 @@ +package ethereum + +import ( + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/samber/lo" +) + +//go:generate go run --mod=mod github.com/fjl/gencodec --type Header --field-override headerMarshal --out type_header.go +type Header struct { + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + UncleHash common.Hash `json:"sha3Uncles"` + Coinbase common.Address `json:"miner"` + Number *big.Int `json:"number"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Timestamp uint64 `json:"timestamp"` + BaseFee *big.Int `json:"baseFeePerGas"` + Transactions []common.Hash `json:"transactions"` +} + +type headerMarshal struct { + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Timestamp hexutil.Uint64 + BaseFee *hexutil.Big +} + +//go:generate go run --mod=mod github.com/fjl/gencodec --type Block --field-override blockMarshal --out type_block.go +type Block struct { + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + UncleHash common.Hash `json:"sha3Uncles"` + Coinbase common.Address `json:"miner"` + Number *big.Int `json:"number"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Timestamp uint64 `json:"timestamp"` + BaseFee *big.Int `json:"baseFeePerGas"` + Transactions []*Transaction `json:"transactions"` +} + +type blockMarshal struct { + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Timestamp hexutil.Uint64 + BaseFee *hexutil.Big +} + +func (b Block) Header() *Header { + header := Header{ + Hash: b.Hash, + ParentHash: b.ParentHash, + UncleHash: b.UncleHash, + Coinbase: b.Coinbase, + Number: b.Number, + GasLimit: b.GasLimit, + GasUsed: b.GasUsed, + Timestamp: b.Timestamp, + BaseFee: b.BaseFee, + Transactions: lo.Map(b.Transactions, func(transaction *Transaction, _ int) common.Hash { + return transaction.Hash + }), + } + + return &header +} + +//go:generate go run --mod=mod github.com/fjl/gencodec --type Transaction --field-override transactionMarshal --out type_transaction.go +type Transaction struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *big.Int `json:"blockNumber"` + From common.Address `json:"from"` + Gas uint64 `json:"gas"` + GasPrice *big.Int `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input []byte `json:"input"` + To *common.Address `json:"to"` + Index uint `json:"index"` + Value *big.Int `json:"value"` + Type uint64 `json:"type"` + ChainID *big.Int `json:"chainId"` +} + +type transactionMarshal struct { + BlockNumber *hexutil.Big + Gas hexutil.Uint64 + GasPrice *hexutil.Big + Input hexutil.Bytes + Index hexutil.Uint + Value *hexutil.Big + Type hexutil.Uint64 + ChainID *hexutil.Big +} + +//go:generate go run --mod=mod github.com/fjl/gencodec --type Receipt --field-override receiptMarshal --out type_receipt.go +type Receipt struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *big.Int `json:"blockNumber"` + ContractAddress *common.Address `json:"contractAddress"` + CumulativeGasUsed uint64 `json:"cumulativeGasUsed"` + EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` + GasUsed uint64 `json:"gasUsed"` + Logs []*Log `json:"logs"` + Status uint64 `json:"status"` + TransactionHash common.Hash `json:"transactionHash"` + TransactionIndex uint `json:"transactionIndex"` +} + +type receiptMarshal struct { + BlockNumber *hexutil.Big + CumulativeGasUsed hexutil.Uint64 + EffectiveGasPrice *hexutil.Big + GasUsed hexutil.Uint64 + Status hexutil.Uint64 + TransactionIndex hexutil.Uint +} + +//go:generate go run --mod=mod github.com/fjl/gencodec --type Log --field-override logMarshal --out type_log.go +type Log struct { + Address common.Address `json:"address"` + Topics []common.Hash `json:"topics"` + Data []byte `json:"data"` + BlockNumber *big.Int `json:"blockNumber"` + TransactionHash common.Hash `json:"transactionHash"` + TransactionIndex uint `json:"transactionIndex"` + BlockHash common.Hash `json:"blockHash"` + Index uint `json:"logIndex"` + Removed bool `json:"removed"` +} + +func (l Log) Export() types.Log { + return types.Log{ + Address: l.Address, + Topics: l.Topics, + Data: l.Data, + BlockNumber: l.BlockNumber.Uint64(), + TxHash: l.TransactionHash, + TxIndex: l.TransactionIndex, + BlockHash: l.BlockHash, + Index: l.Index, + Removed: l.Removed, + } +} + +type logMarshal struct { + Data hexutil.Bytes + BlockNumber *hexutil.Big + TransactionIndex hexutil.Uint + Index hexutil.Uint +} + +//go:generate go run --mod=mod github.com/fjl/gencodec --type TransactionCall --field-override transactionCallMarshal --out type_transaction_call.go +type TransactionCall struct { + From common.Address `json:"from"` + To *common.Address `json:"to,omitempty"` + Gas uint64 `json:"gas,omitempty"` + GasPrice *big.Int `json:"gasPrice,omitempty"` + Value *big.Int `json:"value,omitempty"` + Data []byte `json:"data,omitempty"` +} + +type transactionCallMarshal struct { + Gas hexutil.Uint64 + GasPrice *hexutil.Big + Value *hexutil.Big + Data hexutil.Bytes +} + +func formatBlockNumber(blockNumber *big.Int) string { + switch { + case blockNumber == nil: + return "latest" + case blockNumber.Int64() == -1: + return "pending" + default: + return hexutil.EncodeBig(blockNumber) + } +} + +func formatTransactionCall(callMessage ethereum.CallMsg) TransactionCall { + return TransactionCall{ + From: callMessage.From, + To: callMessage.To, + Gas: callMessage.Gas, + GasPrice: callMessage.GasPrice, + Value: callMessage.Value, + Data: callMessage.Data, + } +} diff --git a/provider/ethereum/type_block.go b/provider/ethereum/type_block.go new file mode 100644 index 00000000..b005d34c --- /dev/null +++ b/provider/ethereum/type_block.go @@ -0,0 +1,92 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*blockMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (b Block) MarshalJSON() ([]byte, error) { + type Block struct { + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + UncleHash common.Hash `json:"sha3Uncles"` + Coinbase common.Address `json:"miner"` + Number *hexutil.Big `json:"number"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp hexutil.Uint64 `json:"timestamp"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Transactions []*Transaction `json:"transactions"` + } + var enc Block + enc.Hash = b.Hash + enc.ParentHash = b.ParentHash + enc.UncleHash = b.UncleHash + enc.Coinbase = b.Coinbase + enc.Number = (*hexutil.Big)(b.Number) + enc.GasLimit = hexutil.Uint64(b.GasLimit) + enc.GasUsed = hexutil.Uint64(b.GasUsed) + enc.Timestamp = hexutil.Uint64(b.Timestamp) + enc.BaseFee = (*hexutil.Big)(b.BaseFee) + enc.Transactions = b.Transactions + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (b *Block) UnmarshalJSON(input []byte) error { + type Block struct { + Hash *common.Hash `json:"hash"` + ParentHash *common.Hash `json:"parentHash"` + UncleHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Number *hexutil.Big `json:"number"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Timestamp *hexutil.Uint64 `json:"timestamp"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Transactions []*Transaction `json:"transactions"` + } + var dec Block + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Hash != nil { + b.Hash = *dec.Hash + } + if dec.ParentHash != nil { + b.ParentHash = *dec.ParentHash + } + if dec.UncleHash != nil { + b.UncleHash = *dec.UncleHash + } + if dec.Coinbase != nil { + b.Coinbase = *dec.Coinbase + } + if dec.Number != nil { + b.Number = (*big.Int)(dec.Number) + } + if dec.GasLimit != nil { + b.GasLimit = uint64(*dec.GasLimit) + } + if dec.GasUsed != nil { + b.GasUsed = uint64(*dec.GasUsed) + } + if dec.Timestamp != nil { + b.Timestamp = uint64(*dec.Timestamp) + } + if dec.BaseFee != nil { + b.BaseFee = (*big.Int)(dec.BaseFee) + } + if dec.Transactions != nil { + b.Transactions = dec.Transactions + } + return nil +} diff --git a/provider/ethereum/type_header.go b/provider/ethereum/type_header.go new file mode 100644 index 00000000..b77d9cba --- /dev/null +++ b/provider/ethereum/type_header.go @@ -0,0 +1,92 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*headerMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (h Header) MarshalJSON() ([]byte, error) { + type Header struct { + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + UncleHash common.Hash `json:"sha3Uncles"` + Coinbase common.Address `json:"miner"` + Number *hexutil.Big `json:"number"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp hexutil.Uint64 `json:"timestamp"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Transactions []common.Hash `json:"transactions"` + } + var enc Header + enc.Hash = h.Hash + enc.ParentHash = h.ParentHash + enc.UncleHash = h.UncleHash + enc.Coinbase = h.Coinbase + enc.Number = (*hexutil.Big)(h.Number) + enc.GasLimit = hexutil.Uint64(h.GasLimit) + enc.GasUsed = hexutil.Uint64(h.GasUsed) + enc.Timestamp = hexutil.Uint64(h.Timestamp) + enc.BaseFee = (*hexutil.Big)(h.BaseFee) + enc.Transactions = h.Transactions + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *Header) UnmarshalJSON(input []byte) error { + type Header struct { + Hash *common.Hash `json:"hash"` + ParentHash *common.Hash `json:"parentHash"` + UncleHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Number *hexutil.Big `json:"number"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Timestamp *hexutil.Uint64 `json:"timestamp"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Transactions []common.Hash `json:"transactions"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Hash != nil { + h.Hash = *dec.Hash + } + if dec.ParentHash != nil { + h.ParentHash = *dec.ParentHash + } + if dec.UncleHash != nil { + h.UncleHash = *dec.UncleHash + } + if dec.Coinbase != nil { + h.Coinbase = *dec.Coinbase + } + if dec.Number != nil { + h.Number = (*big.Int)(dec.Number) + } + if dec.GasLimit != nil { + h.GasLimit = uint64(*dec.GasLimit) + } + if dec.GasUsed != nil { + h.GasUsed = uint64(*dec.GasUsed) + } + if dec.Timestamp != nil { + h.Timestamp = uint64(*dec.Timestamp) + } + if dec.BaseFee != nil { + h.BaseFee = (*big.Int)(dec.BaseFee) + } + if dec.Transactions != nil { + h.Transactions = dec.Transactions + } + return nil +} diff --git a/provider/ethereum/type_log.go b/provider/ethereum/type_log.go new file mode 100644 index 00000000..fda4f93d --- /dev/null +++ b/provider/ethereum/type_log.go @@ -0,0 +1,86 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*logMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (l Log) MarshalJSON() ([]byte, error) { + type Log struct { + Address common.Address `json:"address"` + Topics []common.Hash `json:"topics"` + Data hexutil.Bytes `json:"data"` + BlockNumber *hexutil.Big `json:"blockNumber"` + TransactionHash common.Hash `json:"transactionHash"` + TransactionIndex hexutil.Uint `json:"transactionIndex"` + BlockHash common.Hash `json:"blockHash"` + Index hexutil.Uint `json:"logIndex"` + Removed bool `json:"removed"` + } + var enc Log + enc.Address = l.Address + enc.Topics = l.Topics + enc.Data = l.Data + enc.BlockNumber = (*hexutil.Big)(l.BlockNumber) + enc.TransactionHash = l.TransactionHash + enc.TransactionIndex = hexutil.Uint(l.TransactionIndex) + enc.BlockHash = l.BlockHash + enc.Index = hexutil.Uint(l.Index) + enc.Removed = l.Removed + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (l *Log) UnmarshalJSON(input []byte) error { + type Log struct { + Address *common.Address `json:"address"` + Topics []common.Hash `json:"topics"` + Data *hexutil.Bytes `json:"data"` + BlockNumber *hexutil.Big `json:"blockNumber"` + TransactionHash *common.Hash `json:"transactionHash"` + TransactionIndex *hexutil.Uint `json:"transactionIndex"` + BlockHash *common.Hash `json:"blockHash"` + Index *hexutil.Uint `json:"logIndex"` + Removed *bool `json:"removed"` + } + var dec Log + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address != nil { + l.Address = *dec.Address + } + if dec.Topics != nil { + l.Topics = dec.Topics + } + if dec.Data != nil { + l.Data = *dec.Data + } + if dec.BlockNumber != nil { + l.BlockNumber = (*big.Int)(dec.BlockNumber) + } + if dec.TransactionHash != nil { + l.TransactionHash = *dec.TransactionHash + } + if dec.TransactionIndex != nil { + l.TransactionIndex = uint(*dec.TransactionIndex) + } + if dec.BlockHash != nil { + l.BlockHash = *dec.BlockHash + } + if dec.Index != nil { + l.Index = uint(*dec.Index) + } + if dec.Removed != nil { + l.Removed = *dec.Removed + } + return nil +} diff --git a/provider/ethereum/type_receipt.go b/provider/ethereum/type_receipt.go new file mode 100644 index 00000000..2325f40c --- /dev/null +++ b/provider/ethereum/type_receipt.go @@ -0,0 +1,92 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*receiptMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (r Receipt) MarshalJSON() ([]byte, error) { + type Receipt struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + ContractAddress *common.Address `json:"contractAddress"` + CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Logs []*Log `json:"logs"` + Status hexutil.Uint64 `json:"status"` + TransactionHash common.Hash `json:"transactionHash"` + TransactionIndex hexutil.Uint `json:"transactionIndex"` + } + var enc Receipt + enc.BlockHash = r.BlockHash + enc.BlockNumber = (*hexutil.Big)(r.BlockNumber) + enc.ContractAddress = r.ContractAddress + enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed) + enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice) + enc.GasUsed = hexutil.Uint64(r.GasUsed) + enc.Logs = r.Logs + enc.Status = hexutil.Uint64(r.Status) + enc.TransactionHash = r.TransactionHash + enc.TransactionIndex = hexutil.Uint(r.TransactionIndex) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (r *Receipt) UnmarshalJSON(input []byte) error { + type Receipt struct { + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + ContractAddress *common.Address `json:"contractAddress"` + CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed"` + EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Logs []*Log `json:"logs"` + Status *hexutil.Uint64 `json:"status"` + TransactionHash *common.Hash `json:"transactionHash"` + TransactionIndex *hexutil.Uint `json:"transactionIndex"` + } + var dec Receipt + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.BlockHash != nil { + r.BlockHash = *dec.BlockHash + } + if dec.BlockNumber != nil { + r.BlockNumber = (*big.Int)(dec.BlockNumber) + } + if dec.ContractAddress != nil { + r.ContractAddress = dec.ContractAddress + } + if dec.CumulativeGasUsed != nil { + r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed) + } + if dec.EffectiveGasPrice != nil { + r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice) + } + if dec.GasUsed != nil { + r.GasUsed = uint64(*dec.GasUsed) + } + if dec.Logs != nil { + r.Logs = dec.Logs + } + if dec.Status != nil { + r.Status = uint64(*dec.Status) + } + if dec.TransactionHash != nil { + r.TransactionHash = *dec.TransactionHash + } + if dec.TransactionIndex != nil { + r.TransactionIndex = uint(*dec.TransactionIndex) + } + return nil +} diff --git a/provider/ethereum/type_transaction.go b/provider/ethereum/type_transaction.go new file mode 100644 index 00000000..3a87b3ba --- /dev/null +++ b/provider/ethereum/type_transaction.go @@ -0,0 +1,104 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*transactionMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (t Transaction) MarshalJSON() ([]byte, error) { + type Transaction struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + To *common.Address `json:"to"` + Index hexutil.Uint `json:"index"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + ChainID *hexutil.Big `json:"chainId"` + } + var enc Transaction + enc.BlockHash = t.BlockHash + enc.BlockNumber = (*hexutil.Big)(t.BlockNumber) + enc.From = t.From + enc.Gas = hexutil.Uint64(t.Gas) + enc.GasPrice = (*hexutil.Big)(t.GasPrice) + enc.Hash = t.Hash + enc.Input = t.Input + enc.To = t.To + enc.Index = hexutil.Uint(t.Index) + enc.Value = (*hexutil.Big)(t.Value) + enc.Type = hexutil.Uint64(t.Type) + enc.ChainID = (*hexutil.Big)(t.ChainID) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (t *Transaction) UnmarshalJSON(input []byte) error { + type Transaction struct { + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From *common.Address `json:"from"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash *common.Hash `json:"hash"` + Input *hexutil.Bytes `json:"input"` + To *common.Address `json:"to"` + Index *hexutil.Uint `json:"index"` + Value *hexutil.Big `json:"value"` + Type *hexutil.Uint64 `json:"type"` + ChainID *hexutil.Big `json:"chainId"` + } + var dec Transaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.BlockHash != nil { + t.BlockHash = *dec.BlockHash + } + if dec.BlockNumber != nil { + t.BlockNumber = (*big.Int)(dec.BlockNumber) + } + if dec.From != nil { + t.From = *dec.From + } + if dec.Gas != nil { + t.Gas = uint64(*dec.Gas) + } + if dec.GasPrice != nil { + t.GasPrice = (*big.Int)(dec.GasPrice) + } + if dec.Hash != nil { + t.Hash = *dec.Hash + } + if dec.Input != nil { + t.Input = *dec.Input + } + if dec.To != nil { + t.To = dec.To + } + if dec.Index != nil { + t.Index = uint(*dec.Index) + } + if dec.Value != nil { + t.Value = (*big.Int)(dec.Value) + } + if dec.Type != nil { + t.Type = uint64(*dec.Type) + } + if dec.ChainID != nil { + t.ChainID = (*big.Int)(dec.ChainID) + } + return nil +} diff --git a/provider/ethereum/type_transaction_call.go b/provider/ethereum/type_transaction_call.go new file mode 100644 index 00000000..9959e507 --- /dev/null +++ b/provider/ethereum/type_transaction_call.go @@ -0,0 +1,68 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package ethereum + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*transactionCallMarshal)(nil) + +// MarshalJSON marshals as JSON. +func (t TransactionCall) MarshalJSON() ([]byte, error) { + type TransactionCall struct { + From common.Address `json:"from"` + To *common.Address `json:"to,omitempty"` + Gas hexutil.Uint64 `json:"gas,omitempty"` + GasPrice *hexutil.Big `json:"gasPrice,omitempty"` + Value *hexutil.Big `json:"value,omitempty"` + Data hexutil.Bytes `json:"data,omitempty"` + } + var enc TransactionCall + enc.From = t.From + enc.To = t.To + enc.Gas = hexutil.Uint64(t.Gas) + enc.GasPrice = (*hexutil.Big)(t.GasPrice) + enc.Value = (*hexutil.Big)(t.Value) + enc.Data = t.Data + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (t *TransactionCall) UnmarshalJSON(input []byte) error { + type TransactionCall struct { + From *common.Address `json:"from"` + To *common.Address `json:"to,omitempty"` + Gas *hexutil.Uint64 `json:"gas,omitempty"` + GasPrice *hexutil.Big `json:"gasPrice,omitempty"` + Value *hexutil.Big `json:"value,omitempty"` + Data *hexutil.Bytes `json:"data,omitempty"` + } + var dec TransactionCall + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.From != nil { + t.From = *dec.From + } + if dec.To != nil { + t.To = dec.To + } + if dec.Gas != nil { + t.Gas = uint64(*dec.Gas) + } + if dec.GasPrice != nil { + t.GasPrice = (*big.Int)(dec.GasPrice) + } + if dec.Value != nil { + t.Value = (*big.Int)(dec.Value) + } + if dec.Data != nil { + t.Data = *dec.Data + } + return nil +} From f0feea8083c055b26bc8c9c18938444d391a9815 Mon Sep 17 00:00:00 2001 From: KallyDev Date: Wed, 22 Nov 2023 19:30:54 +0800 Subject: [PATCH 2/6] feat(provider/ethereum): implements token client --- provider/ethereum/token/client.go | 203 +++++++++++++++++++++++++ provider/ethereum/token/client_test.go | 178 ++++++++++++++++++++++ schema/metadata/token.go | 17 +++ 3 files changed, 398 insertions(+) create mode 100644 provider/ethereum/token/client.go create mode 100644 provider/ethereum/token/client_test.go create mode 100644 schema/metadata/token.go diff --git a/provider/ethereum/token/client.go b/provider/ethereum/token/client.go new file mode 100644 index 00000000..99ee4991 --- /dev/null +++ b/provider/ethereum/token/client.go @@ -0,0 +1,203 @@ +package token + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc1155" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc20" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc721" + "github.com/naturalselectionlabs/rss3-node/schema/filter" + "github.com/naturalselectionlabs/rss3-node/schema/metadata" + "github.com/samber/lo" + "github.com/shopspring/decimal" +) + +type LookupFunc = func(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) + +type Client interface { + Lookup(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) +} + +var nativeTokenMap = map[filter.Chain]metadata.Token{ + filter.ChainEthereumMainnet: { + Name: "Ethereum", + Symbol: "ETH", + Decimals: 18, + }, +} + +var _ Client = (*client)(nil) + +type client struct { + ethereumClient ethereum.Client +} + +func (c *client) Lookup(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) { + switch { + case address != nil && id == nil: // ERC-20 token + return c.lookupERC20(ctx, chain, *address, blockNumber) + case address != nil && id != nil: // ERC-721 and ERC-1155 token + return c.lookupNFT(ctx, chain, *address, id, blockNumber) + default: // Native token + return c.lookupNative(ctx, chain, address, id, blockNumber) + } +} + +func (c *client) lookupERC20(ctx context.Context, chain filter.ChainEthereum, address common.Address, blockNumber *big.Int) (*metadata.Token, error) { + tokenMetadata, err := c.lookupERC20ByRPC(ctx, chain, address, blockNumber) + if err != nil { + return nil, fmt.Errorf("lookup ERC20 by rpc: %w", err) + } + + // Fallback NFT approval transactions + standard, err := contract.DetectNFTStandard(ctx, new(big.Int).SetUint64(chain.ID()), address, blockNumber, c.ethereumClient) + if err == nil && standard != contract.StandardUnknown { + tokenMetadata.Standard = standard + } + + return tokenMetadata, nil +} + +func (c *client) lookupERC20ByRPC(ctx context.Context, _ filter.ChainEthereum, address common.Address, blockNumber *big.Int) (*metadata.Token, error) { + tokenMetadata := metadata.Token{ + Address: lo.ToPtr(address.String()), + Standard: contract.StandardERC20, + } + + caller, err := erc20.NewERC20Caller(address, c.ethereumClient) + if err != nil { + return nil, err + } + + callOptions := bind.CallOpts{ + BlockNumber: blockNumber, + Context: ctx, + } + + if tokenMetadata.Name, err = caller.Name(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.Symbol, err = caller.Symbol(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.Decimals, err = caller.Decimals(&callOptions); err != nil { + return nil, err + } + + return &tokenMetadata, nil +} + +func (c *client) lookupNFT(ctx context.Context, chain filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { + chainID := new(big.Int).SetUint64(chain.ID()) + + standard, err := contract.DetectNFTStandard(ctx, chainID, address, blockNumber, c.ethereumClient) + if err != nil { + return nil, fmt.Errorf("detect NFT standard: %w", err) + } + + var tokenMetadata *metadata.Token + + switch standard { + case contract.StandardERC721: + tokenMetadata, err = c.lookupERC721(ctx, chain, address, id, blockNumber) + case contract.StandardERC1155: + tokenMetadata, err = c.lookupERC1155(ctx, chain, address, id, blockNumber) + default: + err = fmt.Errorf("unsupported NFT standard %s", standard) + } + + if err != nil { + return nil, err + } + + return tokenMetadata, nil +} + +func (c *client) lookupERC721(ctx context.Context, _ filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { + tokenMetadata := metadata.Token{ + Address: lo.ToPtr(address.String()), + ID: lo.ToPtr(decimal.NewFromBigInt(id, 0)), + Standard: contract.StandardERC721, + } + + caller, err := erc721.NewERC721Caller(address, c.ethereumClient) + if err != nil { + return nil, err + } + + callOptions := bind.CallOpts{ + BlockNumber: blockNumber, + Context: ctx, + } + + if tokenMetadata.Name, err = caller.Name(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.Symbol, err = caller.Symbol(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.URI, err = caller.TokenURI(&callOptions, id); err != nil { + return nil, err + } + + return &tokenMetadata, nil +} + +func (c *client) lookupERC1155(ctx context.Context, _ filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { + tokenMetadata := metadata.Token{ + Address: lo.ToPtr(address.String()), + ID: lo.ToPtr(decimal.NewFromBigInt(id, 0)), + Standard: contract.StandardERC1155, + } + + caller, err := erc1155.NewERC1155Caller(address, c.ethereumClient) + if err != nil { + return nil, err + } + + callOptions := bind.CallOpts{ + BlockNumber: blockNumber, + Context: ctx, + } + + if tokenMetadata.Name, err = caller.Name(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.Symbol, err = caller.Symbol(&callOptions); err != nil { + return nil, err + } + + if tokenMetadata.URI, err = caller.Uri(&callOptions, id); err != nil { + return nil, err + } + + return &tokenMetadata, nil +} + +func (c *client) lookupNative(_ context.Context, chain filter.ChainEthereum, _ *common.Address, _, _ *big.Int) (*metadata.Token, error) { + tokenMetadata, exists := nativeTokenMap[chain] + if !exists { + return nil, fmt.Errorf("chain %s does not have a native token", chain) + } + + return &tokenMetadata, nil +} + +func NewClient(ethereumClient ethereum.Client) Client { + instance := client{ + ethereumClient: ethereumClient, + } + + return &instance +} diff --git a/provider/ethereum/token/client_test.go b/provider/ethereum/token/client_test.go new file mode 100644 index 00000000..3b879d6a --- /dev/null +++ b/provider/ethereum/token/client_test.go @@ -0,0 +1,178 @@ +package token_test + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/endpoint" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/token" + "github.com/naturalselectionlabs/rss3-node/schema/filter" + "github.com/naturalselectionlabs/rss3-node/schema/metadata" + "github.com/samber/lo" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/require" +) + +func TestEthereumClient(t *testing.T) { + t.Parallel() + + type arguments struct { + chain filter.ChainEthereum + address *common.Address + id *big.Int + } + + var testcases = []struct { + name string + arguments arguments + want metadata.Token + }{ + { + name: "ETH", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + }, + want: metadata.Token{ + Name: "Ethereum", + Symbol: "ETH", + Decimals: 18, + }, + }, + { + name: "RSS3", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0xc98D64DA73a6616c42117b582e832812e7B8D57F")), + }, + want: metadata.Token{ + Address: lo.ToPtr("0xc98D64DA73a6616c42117b582e832812e7B8D57F"), + Name: "RSS3", + Symbol: "RSS3", + Decimals: 18, + Standard: contract.StandardERC20, + }, + }, + { + name: "RSS Fruits Token #61", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0xAcbe98EFe2d4D103e221E04c76D7c55dB15C8E89")), + id: big.NewInt(61), + }, + want: metadata.Token{ + Address: lo.ToPtr("0xAcbe98EFe2d4D103e221E04c76D7c55dB15C8E89"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("61"))), + Name: "RSS Fruits Token", + Symbol: "RFT", + Standard: contract.StandardERC721, + URI: "https://gateway.pinata.cloud/ipfs/QmRjC25urVAke71UYAV4PoMi4mCACcqG7MizjLVnJuVQyw", + }, + }, + { + name: "Base, Introduced #1", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0xD4307E0acD12CF46fD6cf93BC264f5D5D1598792")), + id: big.NewInt(1), + }, + want: metadata.Token{ + Address: lo.ToPtr("0xD4307E0acD12CF46fD6cf93BC264f5D5D1598792"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("1"))), + Name: "Base, Introduced", + Symbol: "BASEINTRODUCED", + Standard: contract.StandardERC721, + URI: "data:application/json;base64,eyJuYW1lIjogIkJhc2UsIEludHJvZHVjZWQgMSIsICJkZXNjcmlwdGlvbiI6ICJNZWV0IEJhc2UsIGFuIEV0aGVyZXVtIEwyIHRoYXQgb2ZmZXJzIGEgc2VjdXJlLCBsb3ctY29zdCwgZGV2ZWxvcGVyLWZyaWVuZGx5IHdheSBmb3IgYW55b25lLCBhbnl3aGVyZSwgdG8gYnVpbGQgZGVjZW50cmFsaXplZCBhcHBzLlxuXG5XZSBjb2xsZWN0aXZlbHkgbWludGVkIOKAmEJhc2UsIEludHJvZHVjZWTigJkgdG8gY2VsZWJyYXRlIHRoZSB0ZXN0bmV0IGxhdW5jaCBhbmQgZ3JvdyB0aGUgYnJvYWRlciBCYXNlIGNvbW11bml0eS4gV2XigJlyZSBleGNpdGVkIHRvIGJ1aWxkIEJhc2UgdG9nZXRoZXIuIiwgImltYWdlIjogImlwZnM6Ly9iYWZ5YmVpYmh0azIzaDZzYXM0eXVhaHR5eTd2Mmt6dndvd3c3aGU0NHJoaXg3a3E0NHJmMmFmM2ZjcSIsICJwcm9wZXJ0aWVzIjogeyJudW1iZXIiOiAxLCAibmFtZSI6ICJCYXNlLCBJbnRyb2R1Y2VkIn19", + }, + }, + { + name: "Cheers UP #0", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0x3113A3c04aEBEC2B77eB38Eabf6a2257B580c54B")), + id: big.NewInt(0), + }, + want: metadata.Token{ + Address: lo.ToPtr("0x3113A3c04aEBEC2B77eB38Eabf6a2257B580c54B"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("0"))), + Name: "Cheers UP", + Symbol: "CUP", + Standard: contract.StandardERC721, + URI: "ipfs://QmR4fuz6w9oKEo6oqwFdTmuXqWwmrsFwv659tSZr1SJiNR", + }, + }, + { + name: "RSS3 Whitepaper #1", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0xB9619cF4F875CdF0E3CE48B28A1C725bC4f6c0fB")), + id: big.NewInt(1), + }, + want: metadata.Token{ + Address: lo.ToPtr("0xB9619cF4F875CdF0E3CE48B28A1C725bC4f6c0fB"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("1"))), + Name: "RSS3 Whitepaper", + Symbol: "RWP", + Standard: contract.StandardERC721, + URI: "ipfs://QmTMD6sLA7M4iegKDhbdMPBZ4HLi5fjW27w2J16gqc5Cb7/1.json", + }, + }, + { + name: "OpenSea Shared Storefront #4452815761501376758038898720702591022279500679302039557361006834352129", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0x495f947276749Ce646f68AC8c248420045cb7b5e")), + id: lo.Must(new(big.Int).SetString("4452815761501376758038898720702591022279500679302039557361006834352129", 0)), + }, + want: metadata.Token{ + Address: lo.ToPtr("0x495f947276749Ce646f68AC8c248420045cb7b5e"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("4452815761501376758038898720702591022279500679302039557361006834352129"))), + Name: "OpenSea Shared Storefront", + Symbol: "OPENSTORE", + Standard: contract.StandardERC1155, + URI: "https://api.opensea.io/api/v1/metadata/0x495f947276749Ce646f68AC8c248420045cb7b5e/0x{id}", + }, + }, + { + name: "Love, Death + Robots", + arguments: arguments{ + chain: filter.ChainEthereumMainnet, + address: lo.ToPtr(common.HexToAddress("0xFD43D1dA000558473822302e1d44D81dA2e4cC0d")), + id: big.NewInt(1), + }, + want: metadata.Token{ + Address: lo.ToPtr("0xFD43D1dA000558473822302e1d44D81dA2e4cC0d"), + ID: lo.ToPtr(lo.Must(decimal.NewFromString("1"))), + Name: "Love, Death + Robots", + Symbol: "LDR", + Standard: contract.StandardERC1155, + URI: "ipfs://QmNjmcjL2cz1LrHmTXy3CpVifRNoy3TDh6duo7jxkFFBZH/{id}", + }, + }, + } + + for _, testcase := range testcases { + testcase := testcase + + t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) + defer cancel() + + ethereumClient, err := ethereum.Dial(ctx, endpoint.MustGet(testcase.arguments.chain)) + require.NoError(t, err) + + tokenClient := token.NewClient(ethereumClient) + + tokenMetadata, err := tokenClient.Lookup(ctx, testcase.arguments.chain, testcase.arguments.address, testcase.arguments.id, nil) + require.NoError(t, err) + + require.Equal(t, testcase.want, *tokenMetadata) + }) + } +} diff --git a/schema/metadata/token.go b/schema/metadata/token.go new file mode 100644 index 00000000..516e572d --- /dev/null +++ b/schema/metadata/token.go @@ -0,0 +1,17 @@ +package metadata + +import ( + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/shopspring/decimal" +) + +type Token struct { + Address *string `json:"address,omitempty"` + ID *decimal.Decimal `json:"id,omitempty"` + Value *decimal.Decimal `json:"value,omitempty"` + Name string `json:"name,omitempty"` + Symbol string `json:"symbol,omitempty"` + URI string `json:"uri,omitempty"` + Decimals uint8 `json:"decimals,omitempty"` + Standard contract.Standard `json:"standard,omitempty"` +} From 869a0e45044aa6ff96116828a314761feeeb593e Mon Sep 17 00:00:00 2001 From: KallyDev Date: Wed, 22 Nov 2023 19:50:29 +0800 Subject: [PATCH 3/6] feat(engine): use a customized ethereum client for source and worker --- go.mod | 1 - go.sum | 27 -- internal/engine/source/ethereum/source.go | 35 +- internal/engine/source/ethereum/task.go | 41 +- .../engine/worker/fallback/ethereum/worker.go | 373 ++++++++++++++++-- .../worker/fallback/ethereum/worker_test.go | 66 ++-- schema/feed.go | 2 +- schema/filter/tag.go | 1 + schema/filter/tag_string.go | 20 +- schema/filter/type_collectible.go | 21 + schema/filter/type_collectible_string.go | 140 +++++++ schema/filter/type_transaction.go | 1 + schema/filter/type_transaction_string.go | 16 +- schema/metadata/collectible.go | 39 ++ schema/metadata/collectible_approval.go | 132 +++++++ schema/metadata/transaction.go | 27 +- schema/metadata/transaction_approval.go | 132 +++++++ 17 files changed, 933 insertions(+), 141 deletions(-) create mode 100644 schema/filter/type_collectible.go create mode 100644 schema/filter/type_collectible_string.go create mode 100644 schema/metadata/collectible.go create mode 100644 schema/metadata/collectible_approval.go create mode 100644 schema/metadata/transaction_approval.go diff --git a/go.mod b/go.mod index b3c29b76..ebe30e46 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/holiman/uint256 v1.2.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 6cc853cd..72c4d19b 100644 --- a/go.sum +++ b/go.sum @@ -89,7 +89,6 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= @@ -113,8 +112,6 @@ github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -132,8 +129,6 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -199,14 +194,10 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -238,18 +229,12 @@ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7 github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -277,9 +262,6 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= @@ -329,12 +311,8 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4= github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -486,7 +464,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= @@ -505,8 +482,6 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -658,8 +633,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/engine/source/ethereum/source.go b/internal/engine/source/ethereum/source.go index e7444343..09f1de1e 100644 --- a/internal/engine/source/ethereum/source.go +++ b/internal/engine/source/ethereum/source.go @@ -6,9 +6,6 @@ import ( "math/big" "time" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/rpc" "github.com/naturalselectionlabs/rss3-node/internal/engine" "github.com/naturalselectionlabs/rss3-node/provider/ethereum" "github.com/naturalselectionlabs/rss3-node/schema/filter" @@ -25,7 +22,7 @@ var _ engine.Source = (*source)(nil) type source struct { config *engine.Config - ethereumClient *ethclient.Client + ethereumClient ethereum.Client state *State } @@ -53,12 +50,8 @@ func (s *source) initialize(ctx context.Context) (err error) { BlockNumber: 0, } - if s.ethereumClient, err = ethclient.Dial(s.config.Endpoint); err != nil { - return fmt.Errorf("dial rpc endpoint: %w", err) - } - - if s.ethereumClient, err = ethclient.Dial(s.config.Endpoint); err != nil { - return fmt.Errorf("dial ethereum endpoint: %w", err) + if s.ethereumClient, err = ethereum.Dial(ctx, s.config.Endpoint); err != nil { + return fmt.Errorf("dial to an ethereum rpc node: %w", err) } chainID, err := s.ethereumClient.ChainID(ctx) @@ -87,13 +80,13 @@ func (s *source) pollBlocks(ctx context.Context, tasksChan chan<- []engine.Task) } // RPC providers may incorrectly shunt the request to a lagging node. - if blockNumberLatestRemote <= blockNumberLatestLocal { + if blockNumberLatestRemote.Uint64() <= blockNumberLatestLocal { zap.L().Info("waiting new block", zap.Uint64("block.number.local", s.state.BlockNumber), zap.Uint64("block.number.remote", blockNumberLatestLocal), zap.Duration("block.time", defaultBlockTime)) time.Sleep(defaultBlockTime) } else { // TODO Need to handle block reorganization. - blockNumberLatestLocal = blockNumberLatestRemote + blockNumberLatestLocal = blockNumberLatestRemote.Uint64() } continue @@ -105,14 +98,14 @@ func (s *source) pollBlocks(ctx context.Context, tasksChan chan<- []engine.Task) } // Before being able to handle block reorganization, correctly handle canonical. - receipts, err := s.ethereumClient.BlockReceipts(ctx, rpc.BlockNumberOrHashWithHash(block.Hash(), true)) + receipts, err := s.ethereumClient.BlockReceipts(ctx, block.Number) if err != nil { - return fmt.Errorf("get receipts by block hash %s: %w", block.Hash(), err) + return fmt.Errorf("get receipts by block number %d: %w", block.Number, err) } tasks, err := s.buildTasks(block, receipts) if err != nil { - return fmt.Errorf("build tasks for block hash: %s: %w", block.Hash(), err) + return fmt.Errorf("build tasks for block hash: %s: %w", block.Hash, err) } // TODO It might be possible to use generics to avoid manual type assertions. @@ -123,20 +116,20 @@ func (s *source) pollBlocks(ctx context.Context, tasksChan chan<- []engine.Task) } } -func (s *source) buildTasks(block *types.Block, receipts types.Receipts) ([]*Task, error) { - tasks := make([]*Task, len(block.Transactions())) +func (s *source) buildTasks(block *ethereum.Block, receipts []*ethereum.Receipt) ([]*Task, error) { + tasks := make([]*Task, len(block.Transactions)) - for index, transaction := range block.Transactions() { + for index, transaction := range block.Transactions { // There is no guarantee that the receipts provided by RPC will be in the same order as the transactions, // so instead of using a transaction index, we can match the hash. - receipt, exists := lo.Find(receipts, func(receipt *types.Receipt) bool { - return receipt.TxHash == transaction.Hash() + receipt, exists := lo.Find(receipts, func(receipt *ethereum.Receipt) bool { + return receipt.TransactionHash == transaction.Hash }) // TODO Often this is also caused by a lagging RPC node failing to get the latest data, // and it's best to fix this before the build task. if !exists { - return nil, fmt.Errorf("no receipt matched to transaction hash %s", transaction.Hash()) + return nil, fmt.Errorf("no receipt matched to transaction hash %s", transaction.Hash) } task := Task{ diff --git a/internal/engine/source/ethereum/task.go b/internal/engine/source/ethereum/task.go index dc36d4fe..9abd731c 100644 --- a/internal/engine/source/ethereum/task.go +++ b/internal/engine/source/ethereum/task.go @@ -19,14 +19,14 @@ var _ engine.Task = (*Task)(nil) type Task struct { Chain filter.ChainEthereum - Header *types.Header - Transaction *types.Transaction + Header *ethereum.Header + Transaction *ethereum.Transaction TransactionIndex uint - Receipt *types.Receipt + Receipt *ethereum.Receipt } func (t Task) ID() string { - return fmt.Sprintf("%s.%s.%s", t.Chain.Network(), t.Chain, t.Transaction.Hash()) + return fmt.Sprintf("%s.%s.%s", t.Chain.Network(), t.Chain, t.Transaction.Hash) } func (t Task) Network() filter.Network { @@ -34,7 +34,7 @@ func (t Task) Network() filter.Network { } func (t Task) Timestamp() uint64 { - return t.Header.Time + return t.Header.Timestamp } func (t Task) Validate() error { @@ -43,24 +43,23 @@ func (t Task) Validate() error { } func (t Task) BuildFeed( /* TODO Implementing options. */ ) (*schema.Feed, error) { - var to common.Address + var to, from common.Address - if t.Transaction.To() == nil { + if t.Transaction.To == nil { // The Nethermind node may be missing this field, // Reference https://github.com/NethermindEth/nethermind/issues/5823. - if t.Receipt.ContractAddress == ethereum.AddressGenesis { - return nil, fmt.Errorf("invalid receipt %s", t.Transaction.Hash().String()) + if t.Receipt.ContractAddress == nil { + return nil, fmt.Errorf("invalid receipt %s", t.Transaction.Hash) } - to = t.Receipt.ContractAddress + to = *t.Receipt.ContractAddress } else { - to = *t.Transaction.To() + to = *t.Transaction.To } - from, err := types.LatestSignerForChainID(t.Transaction.ChainId()).Sender(t.Transaction) - if err != nil { - return nil, fmt.Errorf("recovery transaction sender: %w", err) - } + // TODO Verify transaction signature. + // There is no signer implementation that supports all of layer 2 network at the same time. + from = t.Transaction.From feeAmount, err := t.buildFee() if err != nil { @@ -68,7 +67,7 @@ func (t Task) BuildFeed( /* TODO Implementing options. */ ) (*schema.Feed, error } feed := schema.Feed{ - ID: t.Transaction.Hash().String(), + ID: t.Transaction.Hash.String(), Chain: t.Chain, Index: t.TransactionIndex, From: from.String(), @@ -78,22 +77,22 @@ func (t Task) BuildFeed( /* TODO Implementing options. */ ) (*schema.Feed, error Amount: decimal.NewFromBigInt(feeAmount, 0), Decimal: defaultFeeDecimal, }, - Actions: make([]schema.Action, 0), + Actions: make([]*schema.Action, 0), Status: t.Receipt.Status == types.ReceiptStatusSuccessful, - Timestamp: t.Header.Time, + Timestamp: t.Header.Timestamp, } return &feed, nil } func (t Task) buildFee() (*big.Int, error) { - switch t.Receipt.Type { + switch t.Transaction.Type { case types.LegacyTxType, types.AccessListTxType: - return new(big.Int).Mul(t.Transaction.GasPrice(), new(big.Int).SetUint64(t.Receipt.GasUsed)), nil + return new(big.Int).Mul(t.Transaction.GasPrice, new(big.Int).SetUint64(t.Receipt.GasUsed)), nil case types.DynamicFeeTxType: // TODO Add support for EIP-1559 transaction. return big.NewInt(0), nil default: - return nil, fmt.Errorf("unsupported transaction type %d", t.Receipt.Type) + return nil, fmt.Errorf("unsupported transaction type %d", t.Transaction.Type) } } diff --git a/internal/engine/worker/fallback/ethereum/worker.go b/internal/engine/worker/fallback/ethereum/worker.go index 40437207..04e0bce6 100644 --- a/internal/engine/worker/fallback/ethereum/worker.go +++ b/internal/engine/worker/fallback/ethereum/worker.go @@ -8,8 +8,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/naturalselectionlabs/rss3-node/internal/engine" - "github.com/naturalselectionlabs/rss3-node/internal/engine/source/ethereum" - ethereumx "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + source "github.com/naturalselectionlabs/rss3-node/internal/engine/source/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc1155" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc20" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/contract/erc721" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/token" "github.com/naturalselectionlabs/rss3-node/schema" "github.com/naturalselectionlabs/rss3-node/schema/filter" "github.com/naturalselectionlabs/rss3-node/schema/metadata" @@ -20,15 +25,21 @@ import ( var _ engine.Worker = (*worker)(nil) type worker struct { - config *engine.Config + config *engine.Config + ethereumClient ethereum.Client + tokenClient token.Client + erc20Filterer *erc20.ERC20Filterer + erc721Filterer *erc721.ERC721Filterer + erc1155Filterer *erc1155.ERC1155Filterer } func (w *worker) Match(_ context.Context, task engine.Task) (bool, error) { + // The worker will handle all Ethereum network transactions. return task.Network() == filter.NetworkEthereum, nil } func (w *worker) Transform(ctx context.Context, task engine.Task) (*schema.Feed, error) { - ethereumTask, ok := task.(*ethereum.Task) + ethereumTask, ok := task.(*source.Task) if !ok { return nil, fmt.Errorf("invalid task type: %T", task) } @@ -38,53 +49,357 @@ func (w *worker) Transform(ctx context.Context, task engine.Task) (*schema.Feed, return nil, fmt.Errorf("build feed: %w", err) } - if w.matchEthereumFailedTransaction(ethereumTask) { + // If the transaction is failed, we will not process it. + if w.matchFailedTransaction(ethereumTask) { return feed, nil } - if w.matchEthereumNativeTransferTransaction(ethereumTask) { - action, err := w.handleEthereumNativeTransferTransaction(ctx, ethereumTask) + if w.matchNativeTransferTransaction(ethereumTask) { + action, err := w.handleNativeTransferTransaction(ctx, ethereumTask) if err != nil { return nil, fmt.Errorf("handle native transfer transaction: %w", err) } feed.Type = action.Type - feed.Actions = append(feed.Actions, *action) + feed.Actions = append(feed.Actions, action) + } + + // Handle ERC-20 token transfer logs. + for _, log := range ethereumTask.Receipt.Logs { + // Ignore the log if it is removed or is anonymous event. + if log.Removed || len(log.Topics) == 0 { + continue + } + + var ( + actions []*schema.Action + err error + ) + + switch { + case w.matchERC20TransferLog(ethereumTask, log): + actions, err = w.handleERC20TransferLog(ctx, ethereumTask, log) + case w.matchERC20ApprovalLog(ethereumTask, log): + actions, err = w.handleERC20ApproveLog(ctx, ethereumTask, log) + case w.matchERC721TransferLog(ethereumTask, log): + actions, err = w.handleERC721TransferLog(ctx, ethereumTask, log) + case w.matchERC721ApprovalLog(ethereumTask, log): + actions, err = w.handleERC721ApproveLog(ctx, ethereumTask, log) + case w.matchERC1155TransferLog(ethereumTask, log): + actions, err = w.handleERC1155TransferLog(ctx, ethereumTask, log) + case w.matchERC1155ApprovalLog(ethereumTask, log): + actions, err = w.handleERC1155ApproveLog(ctx, ethereumTask, log) + } + + if err != nil { + return nil, fmt.Errorf("handle ERC-20 transfer log: %w", err) + } + + feed.Actions = append(feed.Actions, actions...) } return feed, nil } -func (w *worker) matchEthereumFailedTransaction(task *ethereum.Task) bool { +func (w *worker) matchFailedTransaction(task *source.Task) bool { return task.Receipt.Status == types.ReceiptStatusFailed } -func (w *worker) matchEthereumNativeTransferTransaction(task *ethereum.Task) bool { - return task.Transaction.Value().Cmp(big.NewInt(0)) == 1 && len(task.Transaction.Data()) == 0 +func (w *worker) matchNativeTransferTransaction(task *source.Task) bool { + return task.Transaction.Value.Cmp(big.NewInt(0)) == 1 && len(task.Transaction.Input) == 0 +} + +func (w *worker) matchERC20TransferLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 3 && contract.MatchEventHashes(log.Topics[0], erc20.EventHashTransfer) +} +func (w *worker) matchERC20ApprovalLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 3 && contract.MatchEventHashes(log.Topics[0], erc20.EventHashApproval) +} + +func (w *worker) matchERC721TransferLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc721.EventHashTransfer) +} + +func (w *worker) matchERC721ApprovalLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc721.EventHashApproval) || + len(log.Topics) == 3 && contract.MatchEventHashes(log.Topics[0], erc721.EventHashApprovalForAll) +} + +func (w *worker) matchERC1155TransferLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc1155.EventHashTransferSingle, erc1155.EventHashTransferBatch) +} + +func (w *worker) matchERC1155ApprovalLog(_ *source.Task, log *ethereum.Log) bool { + return len(log.Topics) == 3 && contract.MatchEventHashes(log.Topics[0], erc1155.EventHashApprovalForAll) +} + +func (w *worker) handleNativeTransferTransaction(ctx context.Context, task *source.Task) (*schema.Action, error) { + transactionFrom := task.Transaction.From + + // If the transaction is a contract creation, + // we will set the from address to the zero address. + var transactionTo = ethereum.AddressGenesis + if task.Transaction.To != nil { + transactionTo = *task.Transaction.To + } + + action, err := w.buildTransactionTransferAction(ctx, task, transactionFrom, transactionTo, nil, task.Transaction.Value) + if err != nil { + return nil, fmt.Errorf("build native transfer action: %w", err) + } + + return action, nil +} + +func (w *worker) handleERC20TransferLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + event, err := w.erc20Filterer.ParseTransfer(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-20 Transfer event: %w", err) + } + + action, err := w.buildTransactionTransferAction(ctx, task, event.From, event.To, &event.Raw.Address, event.Value) + if err != nil { + return nil, fmt.Errorf("build transaction transfer action: %w", err) + } + + actions := []*schema.Action{ + action, + } + + return actions, nil +} + +func (w *worker) handleERC20ApproveLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + event, err := w.erc20Filterer.ParseApproval(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-20 Approval event: %w", err) + } + + action, err := w.buildTransactionApprovalAction(ctx, task, event.Owner, event.Spender, &event.Raw.Address, event.Value) + if err != nil { + return nil, fmt.Errorf("build transaction approval action: %w", err) + } + + actions := []*schema.Action{ + action, + } + + return actions, nil +} + +func (w *worker) handleERC721TransferLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + event, err := w.erc721Filterer.ParseTransfer(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-721 Transfer event: %w", err) + } + + action, err := w.buildCollectibleTransferAction(ctx, task, event.From, event.To, event.Raw.Address, event.TokenId, big.NewInt(1)) + if err != nil { + return nil, fmt.Errorf("build collectible transfer action: %w", err) + } + + actions := []*schema.Action{ + action, + } + + return actions, nil +} + +func (w *worker) handleERC721ApproveLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + var action *schema.Action + + switch { + case len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc721.EventHashApproval): + event, err := w.erc721Filterer.ParseApproval(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-721 Approval event: %w", err) + } + + if action, err = w.buildCollectibleApprovalAction(ctx, task, event.Owner, event.Approved, event.Raw.Address, event.TokenId, nil); err != nil { + return nil, fmt.Errorf("build collectible approval action: %w", err) + } + case len(log.Topics) == 3 && contract.MatchEventHashes(log.Topics[0], erc721.EventHashApprovalForAll): + event, err := w.erc721Filterer.ParseApprovalForAll(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-721 ApprovalForAll event: %w", err) + } + + if action, err = w.buildCollectibleApprovalAction(ctx, task, event.Owner, event.Operator, event.Raw.Address, nil, &event.Approved); err != nil { + return nil, fmt.Errorf("build collectible approval action: %w", err) + } + + default: + return nil, fmt.Errorf("unsupported ERC-721 approval event %s", log.Topics[0]) + } + + actions := []*schema.Action{ + action, + } + + return actions, nil +} + +func (w *worker) handleERC1155TransferLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + actions := make([]*schema.Action, 0) + + switch { + case len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc1155.EventHashTransferSingle): + event, err := w.erc1155Filterer.ParseTransferSingle(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-1577 TransferSingle event: %w", err) + } + + action, err := w.buildCollectibleTransferAction(ctx, task, event.From, event.To, event.Raw.Address, event.Id, event.Value) + if err != nil { + return nil, fmt.Errorf("build collectible transfer action: %w", err) + } + + actions = append(actions, action) + case len(log.Topics) == 4 && contract.MatchEventHashes(log.Topics[0], erc1155.EventHashTransferBatch): + event, err := w.erc1155Filterer.ParseTransferBatch(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-1155 TransferBatch event: %w", err) + } + + if len(event.Ids) != len(event.Values) || len(event.Ids) == 0 { + return nil, fmt.Errorf("invalid ERC-1155 TransferBatch event %s", log.Topics[0]) + } + + for i := range event.Ids { + id, value := event.Ids[i], event.Values[i] + + action, err := w.buildCollectibleTransferAction(ctx, task, event.From, event.To, event.Raw.Address, id, value) + if err != nil { + return nil, fmt.Errorf("build collectible transfer action: %w", err) + } + + actions = append(actions, action) + } + default: + return nil, fmt.Errorf("invalid ERC-1155 transfer event %s", log.Topics[0]) + } + + return actions, nil } -func (w *worker) handleEthereumNativeTransferTransaction(ctx context.Context, task *ethereum.Task) (*schema.Action, error) { - transactionFrom, err := types.LatestSignerForChainID(task.Transaction.ChainId()).Sender(task.Transaction) +func (w *worker) handleERC1155ApproveLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*schema.Action, error) { + event, err := w.erc1155Filterer.ParseApprovalForAll(log.Export()) + if err != nil { + return nil, fmt.Errorf("parse ERC-1155 ApprovalForAll event: %w", err) + } + + action, err := w.buildCollectibleApprovalAction(ctx, task, event.Account, event.Operator, event.Raw.Address, nil, &event.Approved) if err != nil { - return nil, fmt.Errorf("recover transaction signer: %w", err) + return nil, fmt.Errorf("build collectible approval action: %w", err) } - var transactionTo = ethereumx.AddressGenesis - if task.Transaction.To() != nil { - transactionTo = *task.Transaction.To() + actions := []*schema.Action{ + action, } - return w.buildEthereumTransactionTransferAction(ctx, task, transactionFrom, transactionTo, nil, task.Transaction.Value()) + return actions, nil } -func (w *worker) buildEthereumTransactionTransferAction(_ context.Context, _ *ethereum.Task, from, to common.Address, _ *common.Address, tokenValue *big.Int) (*schema.Action, error) { - // TODO Add support for ERC-20 and ERC-1577 token transfer transactions. +func (w *worker) buildTransactionTransferAction(ctx context.Context, task *source.Task, from, to common.Address, tokenAddress *common.Address, tokenValue *big.Int) (*schema.Action, error) { + tokenMetadata, err := w.tokenClient.Lookup(ctx, task.Chain, tokenAddress, nil, task.Header.Number) + if err != nil { + return nil, fmt.Errorf("lookup token %s: %w", tokenAddress, err) + } + + tokenMetadata.Value = lo.ToPtr(decimal.NewFromBigInt(tokenValue, 0)) + action := schema.Action{ - Type: filter.TypeTransactionTransfer, + Type: filter.TypeTransactionTransfer, + From: from.String(), + To: to.String(), + Metadata: metadata.TransactionTransfer(*tokenMetadata), + } + + return &action, nil +} + +func (w *worker) buildTransactionApprovalAction(ctx context.Context, task *source.Task, from, to common.Address, tokenAddress *common.Address, tokenValue *big.Int) (*schema.Action, error) { + tokenMetadata, err := w.tokenClient.Lookup(ctx, task.Chain, tokenAddress, nil, task.Header.Number) + if err != nil { + return nil, fmt.Errorf("lookup token %s: %w", tokenAddress, err) + } + + tokenMetadata.Value = lo.ToPtr(decimal.NewFromBigInt(tokenValue, 0)) + + // Use the token value to determine the action type. + metadataAction := metadata.ActionTransactionApprove + if tokenValue.Cmp(big.NewInt(0)) != 1 { + metadataAction = metadata.ActionTransactionRevoke + } + + action := schema.Action{ + Type: filter.TypeTransactionApproval, + From: from.String(), + To: to.String(), + Metadata: metadata.TransactionApproval{ + Action: metadataAction, + Token: *tokenMetadata, + }, + } + + return &action, nil +} + +func (w *worker) buildCollectibleTransferAction(ctx context.Context, task *source.Task, from, to common.Address, tokenAddress common.Address, tokenID *big.Int, tokenValue *big.Int) (*schema.Action, error) { + tokenMetadata, err := w.tokenClient.Lookup(ctx, task.Chain, &tokenAddress, tokenID, task.Header.Number) + if err != nil { + return nil, fmt.Errorf("lookup token %s: %w", tokenAddress, err) + } + + tokenMetadata.Value = lo.ToPtr(decimal.NewFromBigInt(tokenValue, 0)) + + var actionType filter.CollectibleType + + switch { + case ethereum.IsBurnAddress(to): + actionType = filter.TypeCollectibleBurn + case ethereum.AddressGenesis == from: + actionType = filter.TypeCollectibleMint + default: + actionType = filter.TypeCollectibleTransfer + } + + action := schema.Action{ + Type: actionType, + From: from.String(), + To: to.String(), + Metadata: metadata.CollectibleTransfer(*tokenMetadata), + } + + return &action, nil +} + +func (w *worker) buildCollectibleApprovalAction(ctx context.Context, task *source.Task, from common.Address, to common.Address, tokenAddress common.Address, id *big.Int, approved *bool) (*schema.Action, error) { + tokenMetadata, err := w.tokenClient.Lookup(ctx, task.Chain, &tokenAddress, id, task.Header.Number) + if err != nil { + return nil, fmt.Errorf("lookup token %s: %w", tokenAddress, err) + } + + var metadataAction metadata.CollectibleApprovalAction + + switch { + case approved == nil && to == ethereum.AddressGenesis: + metadataAction = metadata.ActionCollectibleApprovalRevoke + case approved == nil: + metadataAction = metadata.ActionCollectibleApprovalApprove + case *approved: + metadataAction = metadata.ActionCollectibleApprovalApprove + default: + metadataAction = metadata.ActionCollectibleApprovalRevoke + } + + action := schema.Action{ + Type: filter.TypeCollectibleApproval, From: from.String(), To: to.String(), - Metadata: metadata.TransactionTransfer{ - Value: lo.ToPtr(decimal.NewFromBigInt(tokenValue, 0)), + Metadata: metadata.CollectibleApproval{ + Action: metadataAction, + Token: *tokenMetadata, }, } @@ -96,5 +411,17 @@ func NewWorker(config *engine.Config) (engine.Worker, error) { config: config, } + var err error + + if instance.ethereumClient, err = ethereum.Dial(context.Background(), config.Endpoint); err != nil { + return nil, fmt.Errorf("initialize ethereum client: %w", err) + } + + instance.tokenClient = token.NewClient(instance.ethereumClient) + + instance.erc20Filterer = lo.Must(erc20.NewERC20Filterer(ethereum.AddressGenesis, nil)) + instance.erc721Filterer = lo.Must(erc721.NewERC721Filterer(ethereum.AddressGenesis, nil)) + instance.erc1155Filterer = lo.Must(erc1155.NewERC1155Filterer(ethereum.AddressGenesis, nil)) + return &instance, nil } diff --git a/internal/engine/worker/fallback/ethereum/worker_test.go b/internal/engine/worker/fallback/ethereum/worker_test.go index b96eb09b..756a1f2b 100644 --- a/internal/engine/worker/fallback/ethereum/worker_test.go +++ b/internal/engine/worker/fallback/ethereum/worker_test.go @@ -8,8 +8,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/naturalselectionlabs/rss3-node/internal/engine" - "github.com/naturalselectionlabs/rss3-node/internal/engine/source/ethereum" + source "github.com/naturalselectionlabs/rss3-node/internal/engine/source/ethereum" worker "github.com/naturalselectionlabs/rss3-node/internal/engine/worker/fallback/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum" + "github.com/naturalselectionlabs/rss3-node/provider/ethereum/endpoint" "github.com/naturalselectionlabs/rss3-node/schema" "github.com/naturalselectionlabs/rss3-node/schema/filter" "github.com/naturalselectionlabs/rss3-node/schema/metadata" @@ -22,7 +24,7 @@ func TestWorker_Ethereum(t *testing.T) { t.Parallel() type arguments struct { - task *ethereum.Task + task *source.Task config *engine.Config } @@ -35,38 +37,45 @@ func TestWorker_Ethereum(t *testing.T) { { name: "Ethereum native transfer", arguments: arguments{ - task: ðereum.Task{ + task: &source.Task{ Chain: filter.ChainEthereumMainnet, - Header: &types.Header{ + Header: ðereum.Header{ // TODO Provide all fields. - Time: 1647774927, + Timestamp: 1647774927, + }, + Transaction: ðereum.Transaction{ + BlockHash: common.HexToHash("0xea9d0ecd7a085aa998789e8e9c017a7d45f199873380ecb568218525171165b0"), + BlockNumber: lo.Must(hexutil.DecodeBig("0xdc1390")), + From: common.HexToAddress("0x000000A52a03835517E9d193B3c27626e1Bc96b1"), + Gas: 0x5208, + GasPrice: nil, + Hash: common.HexToHash("0x0c2f413efbc243f3bb8edac7e70bdc21936e01401a21b0d63e97732aa80f5d99"), + Input: nil, + To: lo.ToPtr(common.HexToAddress("0xa1b2dcac834117f38fb0356b5176b5693e165c90")), + Index: 0xf4, + Value: lo.Must(hexutil.DecodeBig("0xd48ed9972b634")), + Type: types.DynamicFeeTxType, + ChainID: lo.Must(hexutil.DecodeBig("0x1")), }, - Transaction: types.NewTx(&types.DynamicFeeTx{ - ChainID: lo.Must(hexutil.DecodeBig("0x1")), - Nonce: 0xa, - GasTipCap: lo.Must(hexutil.DecodeBig("0x59682f00")), - GasFeeCap: lo.Must(hexutil.DecodeBig("0x3b8c8f46b")), - Gas: 0x5208, - To: lo.ToPtr(common.HexToAddress("0xa1b2dcac834117f38fb0356b5176b5693e165c90")), - Value: lo.Must(hexutil.DecodeBig("0xd48ed9972b634")), - V: lo.Must(hexutil.DecodeBig("0x0")), - R: lo.Must(hexutil.DecodeBig("0x66f2c1c5fbde05362a8d944ee884ba05777150c5dbe5bd414834b567a73f7765")), - S: lo.Must(hexutil.DecodeBig("0x4b91fdf20d7d85572836d5c4849949d4d96b6cff36e6a53f2b1dce6a3144ef4")), - }), TransactionIndex: 0xf4, - Receipt: &types.Receipt{ - Type: 0x2, - PostState: common.Hex2Bytes("0x1"), - Status: types.ReceiptStatusSuccessful, - CumulativeGasUsed: 0x18b21de, - TxHash: common.HexToHash("0x0c2f413efbc243f3bb8edac7e70bdc21936e01401a21b0d63e97732aa80f5d99"), - GasUsed: 0x5208, - EffectiveGasPrice: lo.Must(hexutil.DecodeBig("0x3b8c8f46b")), + Receipt: ðereum.Receipt{ BlockHash: common.HexToHash("0xea9d0ecd7a085aa998789e8e9c017a7d45f199873380ecb568218525171165b0"), BlockNumber: lo.Must(hexutil.DecodeBig("0xdc1390")), + ContractAddress: nil, + CumulativeGasUsed: 0x18b21de, + EffectiveGasPrice: lo.Must(hexutil.DecodeBig("0x3b8c8f46b")), + GasUsed: 0x5208, + Logs: nil, + Status: types.ReceiptStatusSuccessful, + TransactionHash: common.HexToHash("0x0c2f413efbc243f3bb8edac7e70bdc21936e01401a21b0d63e97732aa80f5d99"), TransactionIndex: 0xf4, }, }, + config: &engine.Config{ + Network: filter.NetworkEthereum.String(), + Chain: filter.ChainEthereumMainnet.String(), + Endpoint: endpoint.MustGet(filter.ChainEthereumMainnet), + }, }, want: &schema.Feed{ ID: "0x0c2f413efbc243f3bb8edac7e70bdc21936e01401a21b0d63e97732aa80f5d99", @@ -79,13 +88,16 @@ func TestWorker_Ethereum(t *testing.T) { Amount: decimal.NewFromInt(0), Decimal: 18, }, - Actions: []schema.Action{ + Actions: []*schema.Action{ { Type: filter.TypeTransactionTransfer, From: "0x000000A52a03835517E9d193B3c27626e1Bc96b1", To: "0xA1b2DCAC834117F38FB0356b5176B5693E165c90", Metadata: metadata.TransactionTransfer{ - Value: lo.ToPtr(lo.Must(decimal.NewFromString("3739360016119348"))), + Value: lo.ToPtr(lo.Must(decimal.NewFromString("3739360016119348"))), + Name: "Ethereum", + Symbol: "ETH", + Decimals: 18, }, }, }, diff --git a/schema/feed.go b/schema/feed.go index 4f21155d..6c13a879 100644 --- a/schema/feed.go +++ b/schema/feed.go @@ -13,7 +13,7 @@ type Feed struct { Type filter.Type `json:"type"` Platform *filter.Platform `json:"platform,omitempty"` Fee Fee `json:"fee"` - Actions []Action `json:"actions"` + Actions []*Action `json:"actions"` Status bool `json:"status"` Timestamp uint64 `json:"timestamp"` } diff --git a/schema/filter/tag.go b/schema/filter/tag.go index 04e9a8eb..e4360a39 100644 --- a/schema/filter/tag.go +++ b/schema/filter/tag.go @@ -6,4 +6,5 @@ type Tag uint64 const ( TagUnknown Tag = iota TagTransaction + TagCollectible ) diff --git a/schema/filter/tag_string.go b/schema/filter/tag_string.go index 3c2f673a..eb36e213 100644 --- a/schema/filter/tag_string.go +++ b/schema/filter/tag_string.go @@ -9,11 +9,11 @@ import ( "strings" ) -const _TagName = "unknowntransaction" +const _TagName = "unknowntransactioncollectible" -var _TagIndex = [...]uint8{0, 7, 18} +var _TagIndex = [...]uint8{0, 7, 18, 29} -const _TagLowerName = "unknowntransaction" +const _TagLowerName = "unknowntransactioncollectible" func (i Tag) String() string { if i >= Tag(len(_TagIndex)-1) { @@ -32,20 +32,24 @@ func _TagNoOp() { var x [1]struct{} _ = x[TagUnknown-(0)] _ = x[TagTransaction-(1)] + _ = x[TagCollectible-(2)] } -var _TagValues = []Tag{TagUnknown, TagTransaction} +var _TagValues = []Tag{TagUnknown, TagTransaction, TagCollectible} var _TagNameToValueMap = map[string]Tag{ - _TagName[0:7]: TagUnknown, - _TagLowerName[0:7]: TagUnknown, - _TagName[7:18]: TagTransaction, - _TagLowerName[7:18]: TagTransaction, + _TagName[0:7]: TagUnknown, + _TagLowerName[0:7]: TagUnknown, + _TagName[7:18]: TagTransaction, + _TagLowerName[7:18]: TagTransaction, + _TagName[18:29]: TagCollectible, + _TagLowerName[18:29]: TagCollectible, } var _TagNames = []string{ _TagName[0:7], _TagName[7:18], + _TagName[18:29], } // TagString retrieves an enum value from the enum constants string name. diff --git a/schema/filter/type_collectible.go b/schema/filter/type_collectible.go new file mode 100644 index 00000000..2cf029af --- /dev/null +++ b/schema/filter/type_collectible.go @@ -0,0 +1,21 @@ +package filter + +//go:generate go run --mod=mod github.com/dmarkham/enumer --values --type=CollectibleType --transform=snake --trimprefix=TypeCollectible --output type_collectible_string.go --json --sql +type CollectibleType uint64 + +//goland:noinspection GoMixedReceiverTypes +func (i CollectibleType) Name() string { + return i.String() +} + +//goland:noinspection GoMixedReceiverTypes +func (i CollectibleType) Tag() Tag { + return TagCollectible +} + +const ( + TypeCollectibleTransfer CollectibleType = iota + 1 + TypeCollectibleApproval + TypeCollectibleMint + TypeCollectibleBurn +) diff --git a/schema/filter/type_collectible_string.go b/schema/filter/type_collectible_string.go new file mode 100644 index 00000000..d0ba6a52 --- /dev/null +++ b/schema/filter/type_collectible_string.go @@ -0,0 +1,140 @@ +// Code generated by "enumer --values --type=CollectibleType --transform=snake --trimprefix=TypeCollectible --output type_collectible_string.go --json --sql"; DO NOT EDIT. + +package filter + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "strings" +) + +const _CollectibleTypeName = "transferapprovalmintburn" + +var _CollectibleTypeIndex = [...]uint8{0, 8, 16, 20, 24} + +const _CollectibleTypeLowerName = "transferapprovalmintburn" + +func (i CollectibleType) String() string { + i -= 1 + if i >= CollectibleType(len(_CollectibleTypeIndex)-1) { + return fmt.Sprintf("CollectibleType(%d)", i+1) + } + return _CollectibleTypeName[_CollectibleTypeIndex[i]:_CollectibleTypeIndex[i+1]] +} + +func (CollectibleType) Values() []string { + return CollectibleTypeStrings() +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _CollectibleTypeNoOp() { + var x [1]struct{} + _ = x[TypeCollectibleTransfer-(1)] + _ = x[TypeCollectibleApproval-(2)] + _ = x[TypeCollectibleMint-(3)] + _ = x[TypeCollectibleBurn-(4)] +} + +var _CollectibleTypeValues = []CollectibleType{TypeCollectibleTransfer, TypeCollectibleApproval, TypeCollectibleMint, TypeCollectibleBurn} + +var _CollectibleTypeNameToValueMap = map[string]CollectibleType{ + _CollectibleTypeName[0:8]: TypeCollectibleTransfer, + _CollectibleTypeLowerName[0:8]: TypeCollectibleTransfer, + _CollectibleTypeName[8:16]: TypeCollectibleApproval, + _CollectibleTypeLowerName[8:16]: TypeCollectibleApproval, + _CollectibleTypeName[16:20]: TypeCollectibleMint, + _CollectibleTypeLowerName[16:20]: TypeCollectibleMint, + _CollectibleTypeName[20:24]: TypeCollectibleBurn, + _CollectibleTypeLowerName[20:24]: TypeCollectibleBurn, +} + +var _CollectibleTypeNames = []string{ + _CollectibleTypeName[0:8], + _CollectibleTypeName[8:16], + _CollectibleTypeName[16:20], + _CollectibleTypeName[20:24], +} + +// CollectibleTypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func CollectibleTypeString(s string) (CollectibleType, error) { + if val, ok := _CollectibleTypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _CollectibleTypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to CollectibleType values", s) +} + +// CollectibleTypeValues returns all values of the enum +func CollectibleTypeValues() []CollectibleType { + return _CollectibleTypeValues +} + +// CollectibleTypeStrings returns a slice of all String values of the enum +func CollectibleTypeStrings() []string { + strs := make([]string, len(_CollectibleTypeNames)) + copy(strs, _CollectibleTypeNames) + return strs +} + +// IsACollectibleType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i CollectibleType) IsACollectibleType() bool { + for _, v := range _CollectibleTypeValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for CollectibleType +func (i CollectibleType) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for CollectibleType +func (i *CollectibleType) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("CollectibleType should be a string, got %s", data) + } + + var err error + *i, err = CollectibleTypeString(s) + return err +} + +func (i CollectibleType) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *CollectibleType) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of CollectibleType: %[1]T(%[1]v)", value) + } + + val, err := CollectibleTypeString(str) + if err != nil { + return err + } + + *i = val + return nil +} diff --git a/schema/filter/type_transaction.go b/schema/filter/type_transaction.go index 73f107a5..5733ecab 100644 --- a/schema/filter/type_transaction.go +++ b/schema/filter/type_transaction.go @@ -15,4 +15,5 @@ func (i TransactionType) Tag() Tag { const ( TypeTransactionTransfer TransactionType = iota + 1 + TypeTransactionApproval ) diff --git a/schema/filter/type_transaction_string.go b/schema/filter/type_transaction_string.go index 078f04e1..269cc1d0 100644 --- a/schema/filter/type_transaction_string.go +++ b/schema/filter/type_transaction_string.go @@ -9,11 +9,11 @@ import ( "strings" ) -const _TransactionTypeName = "transfer" +const _TransactionTypeName = "transferapproval" -var _TransactionTypeIndex = [...]uint8{0, 8} +var _TransactionTypeIndex = [...]uint8{0, 8, 16} -const _TransactionTypeLowerName = "transfer" +const _TransactionTypeLowerName = "transferapproval" func (i TransactionType) String() string { i -= 1 @@ -32,17 +32,21 @@ func (TransactionType) Values() []string { func _TransactionTypeNoOp() { var x [1]struct{} _ = x[TypeTransactionTransfer-(1)] + _ = x[TypeTransactionApproval-(2)] } -var _TransactionTypeValues = []TransactionType{TypeTransactionTransfer} +var _TransactionTypeValues = []TransactionType{TypeTransactionTransfer, TypeTransactionApproval} var _TransactionTypeNameToValueMap = map[string]TransactionType{ - _TransactionTypeName[0:8]: TypeTransactionTransfer, - _TransactionTypeLowerName[0:8]: TypeTransactionTransfer, + _TransactionTypeName[0:8]: TypeTransactionTransfer, + _TransactionTypeLowerName[0:8]: TypeTransactionTransfer, + _TransactionTypeName[8:16]: TypeTransactionApproval, + _TransactionTypeLowerName[8:16]: TypeTransactionApproval, } var _TransactionTypeNames = []string{ _TransactionTypeName[0:8], + _TransactionTypeName[8:16], } // TransactionTypeString retrieves an enum value from the enum constants string name. diff --git a/schema/metadata/collectible.go b/schema/metadata/collectible.go new file mode 100644 index 00000000..c927ac99 --- /dev/null +++ b/schema/metadata/collectible.go @@ -0,0 +1,39 @@ +package metadata + +import ( + "github.com/naturalselectionlabs/rss3-node/schema" + "github.com/naturalselectionlabs/rss3-node/schema/filter" +) + +var _ schema.Metadata = (*CollectibleTransfer)(nil) + +type CollectibleTransfer Token + +func (c CollectibleTransfer) Type() filter.Type { + return filter.TypeCollectibleTransfer +} + +//go:generate go run --mod=mod github.com/dmarkham/enumer --values --type=CollectibleApprovalAction --transform=snake --trimprefix=ActionCollectibleApproval --output collectible_approval.go --json --sql +type CollectibleApprovalAction uint64 + +//goland:noinspection GoMixedReceiverTypes +func (t CollectibleApprovalAction) Type() filter.Type { + return filter.TypeCollectibleApproval +} + +const ( + ActionCollectibleApprovalApprove CollectibleApprovalAction = iota + 1 + ActionCollectibleApprovalRevoke +) + +var _ schema.Metadata = (*CollectibleApproval)(nil) + +type CollectibleApproval struct { + Action CollectibleApprovalAction `json:"action"` + + Token +} + +func (c CollectibleApproval) Type() filter.Type { + return filter.TypeCollectibleApproval +} diff --git a/schema/metadata/collectible_approval.go b/schema/metadata/collectible_approval.go new file mode 100644 index 00000000..a9901433 --- /dev/null +++ b/schema/metadata/collectible_approval.go @@ -0,0 +1,132 @@ +// Code generated by "enumer --values --type=CollectibleApprovalAction --transform=snake --trimprefix=ActionCollectibleApproval --output collectible_approval.go --json --sql"; DO NOT EDIT. + +package metadata + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "strings" +) + +const _CollectibleApprovalActionName = "approverevoke" + +var _CollectibleApprovalActionIndex = [...]uint8{0, 7, 13} + +const _CollectibleApprovalActionLowerName = "approverevoke" + +func (i CollectibleApprovalAction) String() string { + i -= 1 + if i >= CollectibleApprovalAction(len(_CollectibleApprovalActionIndex)-1) { + return fmt.Sprintf("CollectibleApprovalAction(%d)", i+1) + } + return _CollectibleApprovalActionName[_CollectibleApprovalActionIndex[i]:_CollectibleApprovalActionIndex[i+1]] +} + +func (CollectibleApprovalAction) Values() []string { + return CollectibleApprovalActionStrings() +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _CollectibleApprovalActionNoOp() { + var x [1]struct{} + _ = x[ActionCollectibleApprovalApprove-(1)] + _ = x[ActionCollectibleApprovalRevoke-(2)] +} + +var _CollectibleApprovalActionValues = []CollectibleApprovalAction{ActionCollectibleApprovalApprove, ActionCollectibleApprovalRevoke} + +var _CollectibleApprovalActionNameToValueMap = map[string]CollectibleApprovalAction{ + _CollectibleApprovalActionName[0:7]: ActionCollectibleApprovalApprove, + _CollectibleApprovalActionLowerName[0:7]: ActionCollectibleApprovalApprove, + _CollectibleApprovalActionName[7:13]: ActionCollectibleApprovalRevoke, + _CollectibleApprovalActionLowerName[7:13]: ActionCollectibleApprovalRevoke, +} + +var _CollectibleApprovalActionNames = []string{ + _CollectibleApprovalActionName[0:7], + _CollectibleApprovalActionName[7:13], +} + +// CollectibleApprovalActionString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func CollectibleApprovalActionString(s string) (CollectibleApprovalAction, error) { + if val, ok := _CollectibleApprovalActionNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _CollectibleApprovalActionNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to CollectibleApprovalAction values", s) +} + +// CollectibleApprovalActionValues returns all values of the enum +func CollectibleApprovalActionValues() []CollectibleApprovalAction { + return _CollectibleApprovalActionValues +} + +// CollectibleApprovalActionStrings returns a slice of all String values of the enum +func CollectibleApprovalActionStrings() []string { + strs := make([]string, len(_CollectibleApprovalActionNames)) + copy(strs, _CollectibleApprovalActionNames) + return strs +} + +// IsACollectibleApprovalAction returns "true" if the value is listed in the enum definition. "false" otherwise +func (i CollectibleApprovalAction) IsACollectibleApprovalAction() bool { + for _, v := range _CollectibleApprovalActionValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for CollectibleApprovalAction +func (i CollectibleApprovalAction) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for CollectibleApprovalAction +func (i *CollectibleApprovalAction) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("CollectibleApprovalAction should be a string, got %s", data) + } + + var err error + *i, err = CollectibleApprovalActionString(s) + return err +} + +func (i CollectibleApprovalAction) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *CollectibleApprovalAction) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of CollectibleApprovalAction: %[1]T(%[1]v)", value) + } + + val, err := CollectibleApprovalActionString(str) + if err != nil { + return err + } + + *i = val + return nil +} diff --git a/schema/metadata/transaction.go b/schema/metadata/transaction.go index 7706d22b..528ae0a7 100644 --- a/schema/metadata/transaction.go +++ b/schema/metadata/transaction.go @@ -3,17 +3,32 @@ package metadata import ( "github.com/naturalselectionlabs/rss3-node/schema" "github.com/naturalselectionlabs/rss3-node/schema/filter" - "github.com/shopspring/decimal" ) var _ schema.Metadata = (*TransactionTransfer)(nil) -type TransactionTransfer struct { - Address *string `json:"address,omitempty"` - ID *decimal.Decimal `json:"id,omitempty"` - Value *decimal.Decimal `json:"value,omitempty"` -} +type TransactionTransfer Token func (t TransactionTransfer) Type() filter.Type { return filter.TypeTransactionTransfer } + +//go:generate go run --mod=mod github.com/dmarkham/enumer --values --type=TransactionApprovalAction --transform=snake --trimprefix=ActionTransaction --output transaction_approval.go --json --sql +type TransactionApprovalAction uint64 + +const ( + ActionTransactionApprove TransactionApprovalAction = iota + 1 + ActionTransactionRevoke +) + +var _ schema.Metadata = (*TransactionApproval)(nil) + +type TransactionApproval struct { + Action TransactionApprovalAction `json:"action"` + + Token +} + +func (t TransactionApproval) Type() filter.Type { + return filter.TypeTransactionApproval +} diff --git a/schema/metadata/transaction_approval.go b/schema/metadata/transaction_approval.go new file mode 100644 index 00000000..791c18bc --- /dev/null +++ b/schema/metadata/transaction_approval.go @@ -0,0 +1,132 @@ +// Code generated by "enumer --values --type=TransactionApprovalAction --transform=snake --trimprefix=ActionTransaction --output transaction_approval.go --json --sql"; DO NOT EDIT. + +package metadata + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "strings" +) + +const _TransactionApprovalActionName = "approverevoke" + +var _TransactionApprovalActionIndex = [...]uint8{0, 7, 13} + +const _TransactionApprovalActionLowerName = "approverevoke" + +func (i TransactionApprovalAction) String() string { + i -= 1 + if i >= TransactionApprovalAction(len(_TransactionApprovalActionIndex)-1) { + return fmt.Sprintf("TransactionApprovalAction(%d)", i+1) + } + return _TransactionApprovalActionName[_TransactionApprovalActionIndex[i]:_TransactionApprovalActionIndex[i+1]] +} + +func (TransactionApprovalAction) Values() []string { + return TransactionApprovalActionStrings() +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _TransactionApprovalActionNoOp() { + var x [1]struct{} + _ = x[ActionTransactionApprove-(1)] + _ = x[ActionTransactionRevoke-(2)] +} + +var _TransactionApprovalActionValues = []TransactionApprovalAction{ActionTransactionApprove, ActionTransactionRevoke} + +var _TransactionApprovalActionNameToValueMap = map[string]TransactionApprovalAction{ + _TransactionApprovalActionName[0:7]: ActionTransactionApprove, + _TransactionApprovalActionLowerName[0:7]: ActionTransactionApprove, + _TransactionApprovalActionName[7:13]: ActionTransactionRevoke, + _TransactionApprovalActionLowerName[7:13]: ActionTransactionRevoke, +} + +var _TransactionApprovalActionNames = []string{ + _TransactionApprovalActionName[0:7], + _TransactionApprovalActionName[7:13], +} + +// TransactionApprovalActionString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func TransactionApprovalActionString(s string) (TransactionApprovalAction, error) { + if val, ok := _TransactionApprovalActionNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _TransactionApprovalActionNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to TransactionApprovalAction values", s) +} + +// TransactionApprovalActionValues returns all values of the enum +func TransactionApprovalActionValues() []TransactionApprovalAction { + return _TransactionApprovalActionValues +} + +// TransactionApprovalActionStrings returns a slice of all String values of the enum +func TransactionApprovalActionStrings() []string { + strs := make([]string, len(_TransactionApprovalActionNames)) + copy(strs, _TransactionApprovalActionNames) + return strs +} + +// IsATransactionApprovalAction returns "true" if the value is listed in the enum definition. "false" otherwise +func (i TransactionApprovalAction) IsATransactionApprovalAction() bool { + for _, v := range _TransactionApprovalActionValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for TransactionApprovalAction +func (i TransactionApprovalAction) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for TransactionApprovalAction +func (i *TransactionApprovalAction) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("TransactionApprovalAction should be a string, got %s", data) + } + + var err error + *i, err = TransactionApprovalActionString(s) + return err +} + +func (i TransactionApprovalAction) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i *TransactionApprovalAction) Scan(value interface{}) error { + if value == nil { + return nil + } + + var str string + switch v := value.(type) { + case []byte: + str = string(v) + case string: + str = v + case fmt.Stringer: + str = v.String() + default: + return fmt.Errorf("invalid value of TransactionApprovalAction: %[1]T(%[1]v)", value) + } + + val, err := TransactionApprovalActionString(str) + if err != nil { + return err + } + + *i = val + return nil +} From 3d528bff88e8472c9d93ff2a7a3de35efa850b09 Mon Sep 17 00:00:00 2001 From: KallyDev Date: Mon, 4 Dec 2023 18:01:18 +0800 Subject: [PATCH 4/6] feat(source/ethereum): support EIP-1559 transaction fee --- internal/engine/source/ethereum/task.go | 29 +++++++++++++++++++------ provider/ethereum/type.go | 1 + 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/internal/engine/source/ethereum/task.go b/internal/engine/source/ethereum/task.go index 97df4b46..af16ecef 100644 --- a/internal/engine/source/ethereum/task.go +++ b/internal/engine/source/ethereum/task.go @@ -18,10 +18,10 @@ const defaultFeeDecimal = 18 var _ engine.Task = (*Task)(nil) type Task struct { - Chain filter.ChainEthereum - Header *ethereum.Header - Transaction *ethereum.Transaction - Receipt *ethereum.Receipt + Chain filter.ChainEthereum + Header *ethereum.Header + Transaction *ethereum.Transaction + Receipt *ethereum.Receipt } func (t Task) ID() string { @@ -88,9 +88,24 @@ func (t Task) buildFee() (*big.Int, error) { switch t.Transaction.Type { case types.LegacyTxType, types.AccessListTxType: return new(big.Int).Mul(t.Transaction.GasPrice, new(big.Int).SetUint64(t.Receipt.GasUsed)), nil - case types.DynamicFeeTxType: - // TODO Add support for EIP-1559 transaction. - return big.NewInt(0), nil + case types.DynamicFeeTxType: // https://eips.ethereum.org/EIPS/eip-1559 + var ( + gasPrice decimal.Decimal + gasUsed = decimal.NewFromBigInt(new(big.Int).SetUint64(t.Receipt.GasUsed), 0) + ) + + if t.Receipt.EffectiveGasPrice != nil { + gasPrice = decimal.NewFromBigInt(t.Receipt.EffectiveGasPrice, 0) + } else { + var ( + baseFee = decimal.NewFromBigInt(t.Header.BaseFee, 0) + gasTipCap = decimal.NewFromBigInt(t.Transaction.GasTipCap, 0) + ) + + gasPrice = baseFee.Add(gasTipCap) + } + + return gasPrice.Mul(gasUsed).BigInt(), nil default: return nil, fmt.Errorf("unsupported transaction type %d", t.Transaction.Type) } diff --git a/provider/ethereum/type.go b/provider/ethereum/type.go index 0f09136c..ccccac82 100644 --- a/provider/ethereum/type.go +++ b/provider/ethereum/type.go @@ -80,6 +80,7 @@ type Transaction struct { From common.Address `json:"from"` Gas uint64 `json:"gas"` GasPrice *big.Int `json:"gasPrice"` + GasTipCap *big.Int `json:"maxPriorityFeePerGas"` Hash common.Hash `json:"hash"` Input []byte `json:"input"` To *common.Address `json:"to"` From e5d57a7ab6746c8960bcc67aca06e49cc17c7fb6 Mon Sep 17 00:00:00 2001 From: KallyDev Date: Mon, 4 Dec 2023 20:25:16 +0800 Subject: [PATCH 5/6] fix(worker/fallback): invalid test case --- internal/engine/worker/fallback/ethereum/worker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/engine/worker/fallback/ethereum/worker_test.go b/internal/engine/worker/fallback/ethereum/worker_test.go index c3285624..cd094435 100644 --- a/internal/engine/worker/fallback/ethereum/worker_test.go +++ b/internal/engine/worker/fallback/ethereum/worker_test.go @@ -84,7 +84,7 @@ func TestWorker_Ethereum(t *testing.T) { To: "0xA1b2DCAC834117F38FB0356b5176B5693E165c90", Type: filter.TypeTransactionTransfer, Fee: schema.Fee{ - Amount: decimal.NewFromInt(0), + Amount: lo.Must(decimal.NewFromString("335686667463000")), Decimal: 18, }, Actions: []*schema.Action{ From d14930ae5cfa36837cd1d1e8c53646f2c93efe4c Mon Sep 17 00:00:00 2001 From: KallyDev Date: Mon, 4 Dec 2023 21:03:40 +0800 Subject: [PATCH 6/6] style: more code comments --- internal/node/server.go | 2 +- provider/ethereum/client.go | 11 +++++++++++ provider/ethereum/token/client.go | 13 +++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/internal/node/server.go b/internal/node/server.go index ab927228..651d2741 100644 --- a/internal/node/server.go +++ b/internal/node/server.go @@ -127,7 +127,7 @@ func NewServer(ctx context.Context, config *engine.Config, databaseClient databa return nil, fmt.Errorf("new worker: %w", err) } - // Load checkpoint for initialize source. + // Load checkpoint for initialize the source. checkpoint, err := instance.databaseClient.LoadCheckpoint(ctx, instance.id, config.Chain, instance.worker.Name()) if err != nil { return nil, fmt.Errorf("loca checkpoint: %w", err) diff --git a/provider/ethereum/client.go b/provider/ethereum/client.go index b7589cf1..bc6b48bb 100644 --- a/provider/ethereum/client.go +++ b/provider/ethereum/client.go @@ -34,6 +34,7 @@ type client struct { rpcClient *rpc.Client } +// CodeAt returns the contract code of the given account. func (c *client) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { var code hexutil.Bytes if err := c.rpcClient.CallContext(ctx, &code, "eth_getCode", contract, formatBlockNumber(blockNumber)); err != nil { @@ -43,6 +44,7 @@ func (c *client) CodeAt(ctx context.Context, contract common.Address, blockNumbe return code, nil } +// CallContract returns the result of the given transaction call. func (c *client) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { var value hexutil.Bytes if err := c.rpcClient.CallContext(ctx, &value, "eth_call", formatTransactionCall(call), formatBlockNumber(blockNumber)); err != nil { @@ -52,6 +54,7 @@ func (c *client) CallContract(ctx context.Context, call ethereum.CallMsg, blockN return value, nil } +// ChainID returns the chain ID. func (c *client) ChainID(ctx context.Context) (*big.Int, error) { var chainID *hexutil.Big if err := c.rpcClient.CallContext(ctx, &chainID, "eth_chainId"); err != nil { @@ -61,6 +64,7 @@ func (c *client) ChainID(ctx context.Context) (*big.Int, error) { return chainID.ToInt(), nil } +// BlockNumber returns the current block number. func (c *client) BlockNumber(ctx context.Context) (*big.Int, error) { var number *hexutil.Big if err := c.rpcClient.CallContext(ctx, &number, "eth_blockNumber"); err != nil { @@ -70,6 +74,7 @@ func (c *client) BlockNumber(ctx context.Context) (*big.Int, error) { return number.ToInt(), nil } +// HeaderByHash returns the header with the given hash. func (c *client) HeaderByHash(ctx context.Context, hash common.Hash) (*Header, error) { var header Header if err := c.rpcClient.CallContext(ctx, &header, "eth_getBlockByHash", hash, false); err != nil { @@ -79,6 +84,7 @@ func (c *client) HeaderByHash(ctx context.Context, hash common.Hash) (*Header, e return &header, nil } +// HeaderByNumber returns the header with the given number. func (c *client) HeaderByNumber(ctx context.Context, number *big.Int) (*Header, error) { var header Header if err := c.rpcClient.CallContext(ctx, &header, "eth_getBlockByNumber", formatBlockNumber(number), false); err != nil { @@ -88,6 +94,7 @@ func (c *client) HeaderByNumber(ctx context.Context, number *big.Int) (*Header, return &header, nil } +// BlockByHash returns the block with the given hash. func (c *client) BlockByHash(ctx context.Context, hash common.Hash) (*Block, error) { var block Block if err := c.rpcClient.CallContext(ctx, &block, "eth_getBlockByHash", hash, true); err != nil { @@ -97,6 +104,7 @@ func (c *client) BlockByHash(ctx context.Context, hash common.Hash) (*Block, err return &block, nil } +// BlockByNumber returns the block with the given number. func (c *client) BlockByNumber(ctx context.Context, number *big.Int) (*Block, error) { var block Block if err := c.rpcClient.CallContext(ctx, &block, "eth_getBlockByNumber", formatBlockNumber(number), true); err != nil { @@ -106,6 +114,7 @@ func (c *client) BlockByNumber(ctx context.Context, number *big.Int) (*Block, er return &block, nil } +// BlockReceipts returns the receipts of a block by block number. func (c *client) BlockReceipts(ctx context.Context, number *big.Int) ([]*Receipt, error) { var receipts []*Receipt if err := c.rpcClient.CallContext(ctx, &receipts, "eth_getBlockReceipts", formatBlockNumber(number)); err != nil { @@ -115,6 +124,7 @@ func (c *client) BlockReceipts(ctx context.Context, number *big.Int) ([]*Receipt return receipts, nil } +// TransactionByHash returns the transaction with the given hash. func (c *client) TransactionByHash(ctx context.Context, hash common.Hash) (*Transaction, error) { var transaction Transaction if err := c.rpcClient.CallContext(ctx, &transaction, "eth_getTransactionByHash", hash); err != nil { @@ -124,6 +134,7 @@ func (c *client) TransactionByHash(ctx context.Context, hash common.Hash) (*Tran return &transaction, nil } +// TransactionReceipt returns the receipt of a transaction by transaction hash. func (c *client) TransactionReceipt(ctx context.Context, hash common.Hash) (*Receipt, error) { var receipt Receipt if err := c.rpcClient.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash); err != nil { diff --git a/provider/ethereum/token/client.go b/provider/ethereum/token/client.go index 59ab7c3e..0813f7d6 100644 --- a/provider/ethereum/token/client.go +++ b/provider/ethereum/token/client.go @@ -18,12 +18,15 @@ import ( "github.com/shopspring/decimal" ) +// LookupFunc is a function to look up token metadata. type LookupFunc = func(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) +// Client is a client to look up token metadata. type Client interface { Lookup(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) } +// nativeTokenMap is a map of native token metadata. var nativeTokenMap = map[filter.Chain]metadata.Token{ filter.ChainEthereumMainnet: { Name: "Ethereum", @@ -34,10 +37,12 @@ var nativeTokenMap = map[filter.Chain]metadata.Token{ var _ Client = (*client)(nil) +// client is a client to look up token metadata. type client struct { ethereumClient ethereum.Client } +// Lookup looks up token metadata, it supports ERC-20, ERC-721, ERC-1155 and native token. func (c *client) Lookup(ctx context.Context, chain filter.ChainEthereum, address *common.Address, id, blockNumber *big.Int) (*metadata.Token, error) { switch { case address != nil && id == nil: // ERC-20 token @@ -49,6 +54,7 @@ func (c *client) Lookup(ctx context.Context, chain filter.ChainEthereum, address } } +// lookupERC20 looks up ERC-20 token metadata. func (c *client) lookupERC20(ctx context.Context, chain filter.ChainEthereum, address common.Address, blockNumber *big.Int) (*metadata.Token, error) { tokenMetadata, err := c.lookupERC20ByRPC(ctx, chain, address, blockNumber) if err != nil { @@ -64,6 +70,7 @@ func (c *client) lookupERC20(ctx context.Context, chain filter.ChainEthereum, ad return tokenMetadata, nil } +// lookupERC20ByRPC looks up ERC-20 token metadata by RPC. func (c *client) lookupERC20ByRPC(ctx context.Context, _ filter.ChainEthereum, address common.Address, blockNumber *big.Int) (*metadata.Token, error) { tokenMetadata := metadata.Token{ Address: lo.ToPtr(address.String()), @@ -95,9 +102,11 @@ func (c *client) lookupERC20ByRPC(ctx context.Context, _ filter.ChainEthereum, a return &tokenMetadata, nil } +// lookupNFT looks up NFT token metadata, it supports ERC-721 and ERC-1155. func (c *client) lookupNFT(ctx context.Context, chain filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { chainID := new(big.Int).SetUint64(chain.ID()) + // Detect NFT standard by ERC-165. standard, err := contract.DetectNFTStandard(ctx, chainID, address, blockNumber, c.ethereumClient) if err != nil { return nil, fmt.Errorf("detect NFT standard: %w", err) @@ -121,6 +130,7 @@ func (c *client) lookupNFT(ctx context.Context, chain filter.ChainEthereum, addr return tokenMetadata, nil } +// lookupERC721 looks up ERC-721 token metadata. func (c *client) lookupERC721(ctx context.Context, _ filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { tokenMetadata := metadata.Token{ Address: lo.ToPtr(address.String()), @@ -153,6 +163,7 @@ func (c *client) lookupERC721(ctx context.Context, _ filter.ChainEthereum, addre return &tokenMetadata, nil } +// lookupERC1155 looks up ERC-1155 token metadata. func (c *client) lookupERC1155(ctx context.Context, _ filter.ChainEthereum, address common.Address, id *big.Int, blockNumber *big.Int) (*metadata.Token, error) { tokenMetadata := metadata.Token{ Address: lo.ToPtr(address.String()), @@ -185,6 +196,7 @@ func (c *client) lookupERC1155(ctx context.Context, _ filter.ChainEthereum, addr return &tokenMetadata, nil } +// lookupNative looks up native token metadata. func (c *client) lookupNative(_ context.Context, chain filter.ChainEthereum, _ *common.Address, _, _ *big.Int) (*metadata.Token, error) { tokenMetadata, exists := nativeTokenMap[chain] if !exists { @@ -194,6 +206,7 @@ func (c *client) lookupNative(_ context.Context, chain filter.ChainEthereum, _ * return &tokenMetadata, nil } +// NewClient returns a new client. func NewClient(ethereumClient ethereum.Client) Client { instance := client{ ethereumClient: ethereumClient,