diff --git a/netlify.toml b/netlify.toml index 2b21fd374..754bb0f1e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -55,6 +55,10 @@ REACT_APP_L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "0x90271634BCB020e06ea4840C3f7a REACT_APP_L2_GATEWAY_ROUTER_PROXY_ADDR = "0x37A3FA7Ca7Ae8449059d3faff7a8f710BD7b9D2f" REACT_APP_L2_WETH_ADDRESS = "0x5300000000000000000000000000000000000004" REACT_APP_L2_GAS_PRICE_ORACLE = "0x789a11DE6B471272BA5a6231d845533852f446CD" +REACT_APP_L1_ERC721_GATEWAY_PROXY_ADDR="0x4A73D25A4C99CB912acaf6C5B5e554f2982201c5" +REACT_APP_L2_ERC721_GATEWAY_PROXY_ADDR="0xC53D835514780664BCd7eCfcE7c2E5d9554dc41B" +REACT_APP_L1_ERC1155_GATEWAY_PROXY_ADDR="0xa3F5DD3033698c2832C53f3C3Fe6E062F58cD808" +REACT_APP_L2_ERC1155_GATEWAY_PROXY_ADDR="0x4f33B1655619c2C0B7C450128Df760B4365Cb549" [context.develop.environment] # same as deploy-preview @@ -72,6 +76,10 @@ REACT_APP_L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "0x90271634BCB020e06ea4840C3f7a REACT_APP_L2_GATEWAY_ROUTER_PROXY_ADDR = "0x37A3FA7Ca7Ae8449059d3faff7a8f710BD7b9D2f" REACT_APP_L2_WETH_ADDRESS = "0x5300000000000000000000000000000000000004" REACT_APP_L2_GAS_PRICE_ORACLE = "0x789a11DE6B471272BA5a6231d845533852f446CD" +REACT_APP_L1_ERC721_GATEWAY_PROXY_ADDR="0x4A73D25A4C99CB912acaf6C5B5e554f2982201c5" +REACT_APP_L2_ERC721_GATEWAY_PROXY_ADDR="0xC53D835514780664BCd7eCfcE7c2E5d9554dc41B" +REACT_APP_L1_ERC1155_GATEWAY_PROXY_ADDR="0xa3F5DD3033698c2832C53f3C3Fe6E062F58cD808" +REACT_APP_L2_ERC1155_GATEWAY_PROXY_ADDR="0x4f33B1655619c2C0B7C450128Df760B4365Cb549" [context.alpha.environment] # same as main @@ -90,3 +98,7 @@ REACT_APP_ROUTER_ADDRESS = "0x79d6F4BD5267B44a3028DcF6dCD90F55ba18F2c9" REACT_APP_MULTICALL_ADDR = "0xA1281be5BfF2b4e1b25b1ACAf5B76dBA4ECDbD5C" REACT_APP_L2_WETH_ADDRESS = "0x2b17A0Da68382C4B7DEC16Ea6998057a444817e6" REACT_APP_L2_GAS_PRICE_ORACLE = "0x37D61987d0281Fb17DE079C9B8E56B367b1800c4" +REACT_APP_L1_ERC721_GATEWAY_PROXY_ADDR="0x1C441Dfc5C2eD7A2AA8636748A664E59CB029157" +REACT_APP_L1_ERC1155_GATEWAY_PROXY_ADDR="0xd1bE599aaCBC21448fD6373bbc7c1b4c7806f135" +REACT_APP_L2_ERC721_GATEWAY_PROXY_ADDR="0x8Fee20e0C0Ef16f2898a8073531a857D11b9C700" +REACT_APP_L2_ERC1155_GATEWAY_PROXY_ADDR="0xfe5Fc32777646bD123564C41f711FF708Dd48360" diff --git a/src/apis/dynamic.ts b/src/apis/dynamic.ts index 12cc2fab8..88be643ed 100644 --- a/src/apis/dynamic.ts +++ b/src/apis/dynamic.ts @@ -1 +1,3 @@ export const tokenListUrl = param => `https://raw.githubusercontent.com/scroll-tech/token-list/${param}/scroll.tokenlist.json` + +export const nftTokenListUrl = param => `https://raw.githubusercontent.com/scroll-tech/nft-token-list/${param}/scroll.tokenlist.json` diff --git a/src/assets/abis/L1_ERC1155.json b/src/assets/abis/L1_ERC1155.json new file mode 100644 index 000000000..2582d8202 --- /dev/null +++ b/src/assets/abis/L1_ERC1155.json @@ -0,0 +1,491 @@ +[ + { + "inputs": [], + "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": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "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": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "exists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "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": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "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": "mintBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "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": "string", + "name": "newuri", + "type": "string" + } + ], + "name": "setURI", + "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": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/assets/abis/L1_ERC1155_GATEWAY.json b/src/assets/abis/L1_ERC1155_GATEWAY.json new file mode 100644 index 000000000..f16bce1f0 --- /dev/null +++ b/src/assets/abis/L1_ERC1155_GATEWAY.json @@ -0,0 +1,634 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "BatchDepositERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "DepositERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "FinalizeBatchWithdrawERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "FinalizeWithdrawERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_l2Token", + "type": "address" + } + ], + "name": "UpdateTokenMapping", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchDepositERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchDepositERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "counterpart", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "depositERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "depositERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "finalizeBatchWithdrawERC1155", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "finalizeWithdrawERC1155", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_counterpart", + "type": "address" + }, + { + "internalType": "address", + "name": "_messenger", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "router", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMapping", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + } + ], + "name": "updateTokenMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/assets/abis/L1_ERC721.json b/src/assets/abis/L1_ERC721.json new file mode 100644 index 000000000..2f9249ac4 --- /dev/null +++ b/src/assets/abis/L1_ERC721.json @@ -0,0 +1,319 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_DAI", "type": "address" }, + { "internalType": "address", "name": "_WETH", "type": "address" }, + { "internalType": "string", "name": "_URI", "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": "recipient", "type": "address" }], + "name": "FaucetDrained", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "address", "name": "recipient", "type": "address" }], + "name": "FaucetDripped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "operator", "type": "address" }, + { "indexed": false, "internalType": "bool", "name": "status", "type": "bool" } + ], + "name": "OperatorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "operator", "type": "address" }, + { "indexed": false, "internalType": "bool", "name": "status", "type": "bool" } + ], + "name": "SuperOperatorUpdated", + "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": [], + "name": "DAI", + "outputs": [{ "internalType": "contract IERC20", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DAI_AMOUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ETH_AMOUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NFT_COUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "URI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [{ "internalType": "contract IERC20", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH_AMOUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "approvedOperators", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "availableDrips", + "outputs": [ + { "internalType": "uint256", "name": "ethDrips", "type": "uint256" }, + { "internalType": "uint256", "name": "daiDrips", "type": "uint256" }, + { "internalType": "uint256", "name": "wethDrips", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_recipient", "type": "address" }], + "name": "drain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_recipient", "type": "address" }], + "name": "drip", + "outputs": [], + "stateMutability": "nonpayable", + "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": [], + "name": "nftsMinted", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "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": "address", "name": "", "type": "address" }], + "name": "superOperators", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "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" + }, + { + "inputs": [ + { "internalType": "address", "name": "_operator", "type": "address" }, + { "internalType": "bool", "name": "_status", "type": "bool" } + ], + "name": "updateApprovedOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_nftCount", "type": "uint256" }, + { "internalType": "uint256", "name": "_ethAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "_daiAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "_wethAmount", "type": "uint256" } + ], + "name": "updateDripAmounts", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_operator", "type": "address" }, + { "internalType": "bool", "name": "_status", "type": "bool" } + ], + "name": "updateSuperOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "_URI", "type": "string" }], + "name": "updateTokenURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/src/assets/abis/L1_ERC721_GATEWAY.json b/src/assets/abis/L1_ERC721_GATEWAY.json new file mode 100644 index 000000000..7a0e83966 --- /dev/null +++ b/src/assets/abis/L1_ERC721_GATEWAY.json @@ -0,0 +1,517 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + } + ], + "name": "BatchDepositERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "DepositERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + } + ], + "name": "FinalizeBatchWithdrawERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "FinalizeWithdrawERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_l2Token", + "type": "address" + } + ], + "name": "UpdateTokenMapping", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchDepositERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchDepositERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "counterpart", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "depositERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "depositERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + } + ], + "name": "finalizeBatchWithdrawERC721", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "finalizeWithdrawERC721", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_counterpart", + "type": "address" + }, + { + "internalType": "address", + "name": "_messenger", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "router", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMapping", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + } + ], + "name": "updateTokenMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/assets/abis/L2_ERC1155.json b/src/assets/abis/L2_ERC1155.json new file mode 100644 index 000000000..47998ad1f --- /dev/null +++ b/src/assets/abis/L2_ERC1155.json @@ -0,0 +1,583 @@ +[ + { + "inputs": [], + "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": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "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": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "batchBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "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": "batchMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "internalType": "string[]", + "name": "uris", + "type": "string[]" + } + ], + "name": "batchSetTokenURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "burnBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "exists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "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": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "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": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "tokenUri", + "type": "string" + } + ], + "name": "setTokenURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newuri", + "type": "string" + } + ], + "name": "setURI", + "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": "id", + "type": "uint256" + } + ], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/assets/abis/L2_ERC1155_GATEWAY.json b/src/assets/abis/L2_ERC1155_GATEWAY.json new file mode 100644 index 000000000..3978d03fe --- /dev/null +++ b/src/assets/abis/L2_ERC1155_GATEWAY.json @@ -0,0 +1,634 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "BatchWithdrawERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "FinalizeBatchDepositERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FinalizeDepositERC1155", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_l1Token", + "type": "address" + } + ], + "name": "UpdateTokenMapping", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "WithdrawERC1155", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchWithdrawERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchWithdrawERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "counterpart", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "finalizeBatchDepositERC1155", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "finalizeDepositERC1155", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_counterpart", + "type": "address" + }, + { + "internalType": "address", + "name": "_messenger", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "router", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMapping", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + } + ], + "name": "updateTokenMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "withdrawERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "withdrawERC1155", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/src/assets/abis/L2_ERC721.json b/src/assets/abis/L2_ERC721.json new file mode 100644 index 000000000..950471f66 --- /dev/null +++ b/src/assets/abis/L2_ERC721.json @@ -0,0 +1,512 @@ +[ + { + "inputs": [], + "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": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "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": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "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": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + } + ], + "name": "mintBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "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": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "name": "setTokenURI", + "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": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "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": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/assets/abis/L2_ERC721_GATEWAY.json b/src/assets/abis/L2_ERC721_GATEWAY.json new file mode 100644 index 000000000..17f0df3be --- /dev/null +++ b/src/assets/abis/L2_ERC721_GATEWAY.json @@ -0,0 +1,517 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + } + ], + "name": "BatchWithdrawERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "tokenIds", + "type": "uint256[]" + } + ], + "name": "FinalizeBatchDepositERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "FinalizeDepositERC721", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_l1Token", + "type": "address" + } + ], + "name": "UpdateTokenMapping", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l1Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "l2Token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "WithdrawERC721", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchWithdrawERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "batchWithdrawERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "counterpart", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + } + ], + "name": "finalizeBatchDepositERC721", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "finalizeDepositERC721", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_counterpart", + "type": "address" + }, + { + "internalType": "address", + "name": "_messenger", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "router", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMapping", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l2Token", + "type": "address" + }, + { + "internalType": "address", + "name": "_l1Token", + "type": "address" + } + ], + "name": "updateTokenMapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "withdrawERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "withdrawERC721", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/src/assets/svgs/check.svg b/src/assets/svgs/check.svg new file mode 100644 index 000000000..aa1147d62 --- /dev/null +++ b/src/assets/svgs/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/close.svg b/src/assets/svgs/close.svg new file mode 100644 index 000000000..4d6784ad6 --- /dev/null +++ b/src/assets/svgs/close.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svgs/empty-img.svg b/src/assets/svgs/empty-img.svg new file mode 100644 index 000000000..68a9ba542 --- /dev/null +++ b/src/assets/svgs/empty-img.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/success.svg b/src/assets/svgs/success.svg new file mode 100644 index 000000000..c7618b4f0 --- /dev/null +++ b/src/assets/svgs/success.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/ArticleCard/index.tsx b/src/components/ArticleCard/index.tsx index fe513ed37..826acc638 100644 --- a/src/components/ArticleCard/index.tsx +++ b/src/components/ArticleCard/index.tsx @@ -101,7 +101,7 @@ const ArticleCard = ({ blog, small = false }) => { {blog.summary} - diff --git a/src/components/Header/constants.ts b/src/components/Header/constants.ts index 841584055..07483d7ff 100644 --- a/src/components/Header/constants.ts +++ b/src/components/Header/constants.ts @@ -19,6 +19,11 @@ const navigations = [ key: "bridge", href: "/bridge", }, + { + label: "NFT Bridge", + key: "nft-bridge", + href: "/nft-bridge", + }, { label: "Ecosystem", key: "ecosystem", diff --git a/src/components/LoadingButton/index.tsx b/src/components/LoadingButton/index.tsx index 43d4f5ac7..f363c456c 100644 --- a/src/components/LoadingButton/index.tsx +++ b/src/components/LoadingButton/index.tsx @@ -18,17 +18,17 @@ const ScrollLoadingButton = props => { return null } if (loadingText) { - return loadingText + return {loadingText} } - return children + return {children} } return ( - {renderLoadingText()} + + {renderLoadingText()} } diff --git a/src/components/TextIconButton/index.tsx b/src/components/TextIconButton/index.tsx new file mode 100644 index 000000000..b88c55105 --- /dev/null +++ b/src/components/TextIconButton/index.tsx @@ -0,0 +1,47 @@ +import { makeStyles } from "tss-react/mui" + +import { SvgIcon, Typography } from "@mui/material" +import { ButtonProps } from "@mui/material/Button" + +const useStyles = makeStyles()(() => { + return { + button: { + appearance: "none", + border: 0, + outline: 0, + padding: 0, + margin: 0, + background: "none", + display: "inline-flex", + justifyContent: "center", + alignItems: "center", + "&:hover": { + cursor: "pointer", + }, + }, + label: { + marginLeft: "0.5em", + fontWeight: 600, + }, + } +}) + +export type Props = ButtonProps & { + label: string + icon: any + viewBox: string +} + +const MiniButton = (props: Props) => { + const { icon, viewBox, label, className, ...restProps } = props + const { classes, cx } = useStyles() + + return ( + + ) +} + +export default MiniButton diff --git a/src/components/WalletIndicator/index.tsx b/src/components/WalletIndicator/index.tsx new file mode 100644 index 000000000..4388b27b4 --- /dev/null +++ b/src/components/WalletIndicator/index.tsx @@ -0,0 +1,73 @@ +import { useRef } from "react" + +import { Backdrop, Button, ClickAwayListener, Popper } from "@mui/material" + +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import { truncateAddress } from "@/utils" + +const WalletIndicator = props => { + const { popup = false, children, open, onClose, onOpen } = props + const { walletCurrentAddress, connectWallet } = useWeb3Context() + const buttonRef = useRef(null) + + return ( + <> + {walletCurrentAddress ? ( + <> + {popup ? ( + <> + + [theme.zIndex.modal - 1, -1], + }} + > + + <> + +
{children}
+
+ +
+
+ + ) : ( + + )} + + ) : ( + + )} + + ) +} + +export default WalletIndicator diff --git a/src/constants/common.ts b/src/constants/common.ts index 58490c730..c57cda1e6 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -10,4 +10,9 @@ export const RPCUrl = { SCROLL_LAYER_2: requireEnv("REACT_APP_EXTERNAL_RPC_URI_L2"), } +export const BLOCK_EXPLORER = { + [ChainId.SCROLL_LAYER_1]: requireEnv("REACT_APP_EXTERNAL_EXPLORER_URI_L1"), + [ChainId.SCROLL_LAYER_2]: requireEnv("REACT_APP_EXTERNAL_EXPLORER_URI_L2"), +} + export const ETH_SYMBOL = requireEnv("REACT_APP_ETH_SYMBOL") diff --git a/src/constants/gateway.ts b/src/constants/gateway.ts index 361d20da7..dc4d75d01 100644 --- a/src/constants/gateway.ts +++ b/src/constants/gateway.ts @@ -12,6 +12,12 @@ const StandardERC20GatewayProxyAddr = { const GatewayRouterProxyAddr = { [ChainId.SCROLL_LAYER_1]: requireEnv("REACT_APP_L1_GATEWAY_ROUTER_PROXY_ADDR"), [ChainId.SCROLL_LAYER_2]: requireEnv("REACT_APP_L2_GATEWAY_ROUTER_PROXY_ADDR"), + + [`${ChainId.SCROLL_LAYER_1}_721`]: requireEnv("REACT_APP_L1_ERC721_GATEWAY_PROXY_ADDR"), + [`${ChainId.SCROLL_LAYER_2}_721`]: requireEnv("REACT_APP_L2_ERC721_GATEWAY_PROXY_ADDR"), + + [`${ChainId.SCROLL_LAYER_1}_1155`]: requireEnv("REACT_APP_L1_ERC1155_GATEWAY_PROXY_ADDR"), + [`${ChainId.SCROLL_LAYER_2}_1155`]: requireEnv("REACT_APP_L2_ERC1155_GATEWAY_PROXY_ADDR"), } export { StandardERC20GatewayProxyAddr, GatewayRouterProxyAddr } diff --git a/src/constants/index.ts b/src/constants/index.ts index 34d6ac68f..ab4802dfc 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -113,6 +113,7 @@ export * from "./gateway" export * from "./medias" export * from "./networks" export * from "./transaction" +export * from "./token" export { addresses, navigation, documentation, TESTNET_NAME, ModalStatus } export let l1ExplorerUrl = requireEnv("REACT_APP_EXTERNAL_EXPLORER_URI_L1") diff --git a/src/constants/token.ts b/src/constants/token.ts new file mode 100644 index 000000000..29925e330 --- /dev/null +++ b/src/constants/token.ts @@ -0,0 +1,4 @@ +export const TOEKN_TYPE = { + 721: "ERC721", + 1155: "ERC1155", +} diff --git a/src/constants/transaction.ts b/src/constants/transaction.ts index 71d75dfca..99b2f06e9 100644 --- a/src/constants/transaction.ts +++ b/src/constants/transaction.ts @@ -9,6 +9,7 @@ export enum GasLimit { DEPOSIT_ERC20 = 8e4, WITHDRAW_ETH = 16e4, WITHDRAW_ERC20 = 32e4, + BASE_NFT = 2e5, } export enum TxStatus { diff --git a/src/contexts/AppContextProvider.tsx b/src/contexts/AppContextProvider.tsx index 7bcf24c64..d52238ea4 100644 --- a/src/contexts/AppContextProvider.tsx +++ b/src/contexts/AppContextProvider.tsx @@ -6,7 +6,11 @@ import useSWR from "swr" import { Alert, Snackbar } from "@mui/material" import { tokenListUrl } from "@/apis/dynamic" +import L1_ERC721_GATEWAY_ABI from "@/assets/abis/L1_ERC721_GATEWAY.json" +import L1_ERC1155_GATEWAY_ABI from "@/assets/abis/L1_ERC1155_GATEWAY.json" import L1_GATEWAY_ROUTER_PROXY_ABI from "@/assets/abis/L1_GATEWAY_ROUTER_PROXY_ADDR.json" +import L2_ERC721_GATEWAY_ABI from "@/assets/abis/L2_ERC721_GATEWAY.json" +import L2_ERC1155_GATEWAY_ABI from "@/assets/abis/L2_ERC1155_GATEWAY.json" import L2_GATEWAY_ROUTER_PROXY_ABI from "@/assets/abis/L2_GATEWAY_ROUTER_PROXY_ADDR.json" import { ChainId, ETH_SYMBOL, GatewayRouterProxyAddr, RPCUrl } from "@/constants" import { Token, nativeTokenList, networks } from "@/constants/networks" @@ -39,19 +43,34 @@ const AppContextProvider = ({ children }: any) => { const txHistory = useTxHistory(networksAndSigners) const update = async (web3Provider: BrowserProvider, address: string) => { - let l1signer, l2signer, l1Gateway, l2Gateway, l1Provider, l2Provider, l1ProviderForSafeBlock + let l1signer, + l2signer, + l1Gateway, + l2Gateway, + l1Provider, + l2Provider, + l1ProviderForSafeBlock, + l1Gateway_721, + l2Gateway_721, + l1Gateway_1155, + l2Gateway_1155 + if (chainId === ChainId.SCROLL_LAYER_1) { l1Provider = web3Provider l2Provider = await new JsonRpcProvider(RPCUrl.SCROLL_LAYER_2) l1signer = await web3Provider.getSigner(0) l2signer = new JsonRpcSigner(l2Provider, address) l1Gateway = new ethers.Contract(GatewayRouterProxyAddr[ChainId.SCROLL_LAYER_1], L1_GATEWAY_ROUTER_PROXY_ABI, l1signer) + l1Gateway_721 = new ethers.Contract(GatewayRouterProxyAddr[`${ChainId.SCROLL_LAYER_1}_721`], L1_ERC721_GATEWAY_ABI, l1signer) + l1Gateway_1155 = new ethers.Contract(GatewayRouterProxyAddr[`${ChainId.SCROLL_LAYER_1}_1155`], L1_ERC1155_GATEWAY_ABI, l1signer) } else if (chainId === ChainId.SCROLL_LAYER_2) { l1Provider = await new JsonRpcProvider(RPCUrl.SCROLL_LAYER_1) l2Provider = web3Provider l1signer = new JsonRpcSigner(l1Provider, address) l2signer = await web3Provider.getSigner(0) l2Gateway = new ethers.Contract(GatewayRouterProxyAddr[ChainId.SCROLL_LAYER_2], L2_GATEWAY_ROUTER_PROXY_ABI, l2signer) + l2Gateway_721 = new ethers.Contract(GatewayRouterProxyAddr[`${ChainId.SCROLL_LAYER_2}_721`], L2_ERC721_GATEWAY_ABI, l2signer) + l2Gateway_1155 = new ethers.Contract(GatewayRouterProxyAddr[`${ChainId.SCROLL_LAYER_2}_1155`], L2_ERC1155_GATEWAY_ABI, l2signer) } else { l1Provider = await new JsonRpcProvider(RPCUrl.SCROLL_LAYER_1) l2Provider = await new JsonRpcProvider(RPCUrl.SCROLL_LAYER_2) @@ -65,6 +84,8 @@ const AppContextProvider = ({ children }: any) => { provider: l1Provider, signer: l1signer, gateway: l1Gateway, + gateway_721: l1Gateway_721, + gateway_1155: l1Gateway_1155, }, [`${ChainId.SCROLL_LAYER_1}ForSafeBlock`]: { provider: l1ProviderForSafeBlock, @@ -74,6 +95,8 @@ const AppContextProvider = ({ children }: any) => { provider: l2Provider, signer: l2signer, gateway: l2Gateway, + gateway_721: l2Gateway_721, + gateway_1155: l2Gateway_1155, }, }) } diff --git a/src/contexts/NFTBridgeProvider.tsx b/src/contexts/NFTBridgeProvider.tsx new file mode 100644 index 000000000..269ca371f --- /dev/null +++ b/src/contexts/NFTBridgeProvider.tsx @@ -0,0 +1,71 @@ +import { ethers } from "ethers" +import { createContext, useContext, useMemo } from "react" + +import L1_ERC721 from "@/assets/abis/L1_ERC721.json" +import L1_ERC1155 from "@/assets/abis/L1_ERC1155.json" +import L2_ERC721 from "@/assets/abis/L2_ERC721.json" +import L2_ERC1155 from "@/assets/abis/L2_ERC1155.json" +import { ChainId, TOEKN_TYPE } from "@/constants" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import { useAsyncMemo } from "@/hooks" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { requireEnv } from "@/utils" + +const GATEWAY_721_L1 = requireEnv("REACT_APP_L1_ERC721_GATEWAY_PROXY_ADDR") +const GATEWAY_721_L2 = requireEnv("REACT_APP_L2_ERC721_GATEWAY_PROXY_ADDR") + +const GATEWAY_1155_L1 = requireEnv("REACT_APP_L1_ERC1155_GATEWAY_PROXY_ADDR") +const GATEWAY_1155_L2 = requireEnv("REACT_APP_L2_ERC1155_GATEWAY_PROXY_ADDR") + +const NFTBridgeContext = createContext({}) + +const NFTBridgeProvider = props => { + const { children } = props + const { provider, checkConnectedChainId } = useWeb3Context() + + const { contract } = useNFTBridgeStore() + + const isLayer1 = useMemo(() => checkConnectedChainId(ChainId.SCROLL_LAYER_1), [checkConnectedChainId]) + + const tokenInstance = useAsyncMemo(async () => { + if (provider) { + const signer = await provider.getSigner(0) + if (contract?.type === TOEKN_TYPE[721] && checkConnectedChainId(ChainId.SCROLL_LAYER_1)) { + return new ethers.Contract(contract.l1 as string, L1_ERC721, signer) + } else if (contract?.type === TOEKN_TYPE[721] && checkConnectedChainId(ChainId.SCROLL_LAYER_2)) { + return new ethers.Contract(contract.l2 as string, L2_ERC721, signer) + } else if (contract?.type === TOEKN_TYPE[1155] && checkConnectedChainId(ChainId.SCROLL_LAYER_1)) { + return new ethers.Contract(contract.l1 as string, L1_ERC1155, signer) + } else if (contract?.type === TOEKN_TYPE[1155] && checkConnectedChainId(ChainId.SCROLL_LAYER_2)) { + return new ethers.Contract(contract.l2 as string, L2_ERC1155, signer) + } + return null + } + return null + }, [provider, checkConnectedChainId, contract?.type]) + + const gatewayAddress = useMemo(() => { + if (contract?.type === TOEKN_TYPE[721] && checkConnectedChainId(ChainId.SCROLL_LAYER_1)) { + return GATEWAY_721_L1 + } else if (contract?.type === TOEKN_TYPE[721] && checkConnectedChainId(ChainId.SCROLL_LAYER_2)) { + return GATEWAY_721_L2 + } else if (contract?.type === TOEKN_TYPE[1155] && checkConnectedChainId(ChainId.SCROLL_LAYER_1)) { + return GATEWAY_1155_L1 + } else if (contract?.type === TOEKN_TYPE[1155] && checkConnectedChainId(ChainId.SCROLL_LAYER_2)) { + return GATEWAY_1155_L2 + } + return "" + }, [checkConnectedChainId, contract?.type]) + + return {children} +} + +export function useNFTBridgeContext() { + const ctx = useContext(NFTBridgeContext) + if (ctx === undefined) { + throw new Error("useNFTBridgeContext must be used within Web3Provider") + } + return ctx +} + +export default NFTBridgeProvider diff --git a/src/hooks/useGasFee.ts b/src/hooks/useGasFee.ts new file mode 100644 index 000000000..35d5432af --- /dev/null +++ b/src/hooks/useGasFee.ts @@ -0,0 +1,48 @@ +import { ethers } from "ethers" +import { useMemo } from "react" + +import L1GasPriceOracle from "@/assets/abis/L1GasPriceOracle.json" +import L2GasPriceOracle from "@/assets/abis/L2GasPriceOracle.json" +import { ChainId, GasLimit } from "@/constants" +import { useApp } from "@/contexts/AppContextProvider" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { requireEnv } from "@/utils" + +import useAsyncMemo from "./useAsyncMemo" + +const L1_PRICE_ORACLE = requireEnv("REACT_APP_L2_GAS_PRICE_ORACLE") +const L2_PRICE_ORACLE = requireEnv("REACT_APP_L1_GAS_PRICE_ORACLE") + +const useNFTGasFee = () => { + const { checkConnectedChainId } = useWeb3Context() + const { networksAndSigners } = useApp() + const selectedTokenAmount = useNFTBridgeStore(state => state.selectedTokenAmount()) + + const gasPrice = useAsyncMemo(async () => { + let instance + if (checkConnectedChainId(ChainId.SCROLL_LAYER_1) && networksAndSigners[ChainId.SCROLL_LAYER_1].signer) { + instance = new ethers.Contract(L1_PRICE_ORACLE, L2GasPriceOracle, networksAndSigners[ChainId.SCROLL_LAYER_1].signer) + return await instance.l2BaseFee() + } else if (checkConnectedChainId(ChainId.SCROLL_LAYER_2) && networksAndSigners[ChainId.SCROLL_LAYER_2].signer) { + instance = new ethers.Contract(L2_PRICE_ORACLE, L1GasPriceOracle, networksAndSigners[ChainId.SCROLL_LAYER_2].signer) + return await instance.l1BaseFee() + } + return null + }, [networksAndSigners, checkConnectedChainId]) + + const gasLimit = useMemo(() => { + return selectedTokenAmount * GasLimit.BASE_NFT + }, [selectedTokenAmount]) + + const gasFee = useMemo(() => { + if (gasPrice) { + return gasPrice * BigInt(gasLimit) + } + return null + }, [gasLimit, gasPrice]) + + return { gasLimit, gasFee } +} + +export default useNFTGasFee diff --git a/src/hooks/useNFTApprove.ts b/src/hooks/useNFTApprove.ts new file mode 100644 index 000000000..cc1b51b66 --- /dev/null +++ b/src/hooks/useNFTApprove.ts @@ -0,0 +1,20 @@ +import { useWeb3Context } from "@/contexts/Web3ContextProvider" + +const useNFTApprove = () => { + const { walletCurrentAddress } = useWeb3Context() + + const setApproval = async (tokenInstance, operatorAddress) => { + const tx = await tokenInstance["setApprovalForAll(address,bool)"](operatorAddress, true) + const txResult = await tx.wait() + return txResult + } + + const checkApproval = async (tokenInstance, operatorAddress) => { + const isApproved = await tokenInstance["isApprovedForAll(address,address)"](walletCurrentAddress, operatorAddress) + return isApproved + } + + return { setApproval, checkApproval } +} + +export default useNFTApprove diff --git a/src/pages/bridge/Send/index.tsx b/src/pages/bridge/Send/index.tsx index 9a2da817d..0b0a9875f 100644 --- a/src/pages/bridge/Send/index.tsx +++ b/src/pages/bridge/Send/index.tsx @@ -109,6 +109,7 @@ const Send: FC = () => { const handleTotalBonderFeeDisplay = async () => { if (networksAndSigners[fromNetwork.chainId]?.signer) { const fee = await getPriceFee(fromToken, fromNetwork.isLayer1) + const display = fromTokenAmount ? toTokenDisplay(fee) + " " + ETH_SYMBOL : "-" setTotalBonderFeeDisplay(display) } @@ -214,6 +215,8 @@ const Send: FC = () => { } }, [fromNetwork, fromToken, fromTokenAmount, checkApproval]) + console.log(needsApproval, "needsApproval") + const approveFromToken = async () => { // eslint-disable-next-line const parsedAmount = amountToBN(fromTokenAmount, fromToken.decimals) diff --git a/src/pages/bridge/components/TxTable/index.tsx b/src/pages/bridge/components/TxTable/index.tsx index c3695e849..f42133f00 100644 --- a/src/pages/bridge/components/TxTable/index.tsx +++ b/src/pages/bridge/components/TxTable/index.tsx @@ -26,7 +26,7 @@ import { TxStatus } from "@/constants" import { useApp } from "@/contexts/AppContextProvider" import useTokenInfo from "@/hooks/useTokenInfo" import useTxStore from "@/stores/txStore" -import { generateExploreLink, toTokenDisplay, truncateHash } from "@/utils" +import { generateTxLink, toTokenDisplay, truncateHash } from "@/utils" import StatusChip from "./StatusChip" @@ -230,7 +230,7 @@ const TxRow = props => { From {tx.fromName}: - + {truncateHash(tx.hash)} @@ -241,7 +241,7 @@ const TxRow = props => { To {tx.toName}: {tx.toHash ? ( - + {truncateHash(tx.toHash)} ) : ( diff --git a/src/pages/nftBridge/Header/CopyButton.tsx b/src/pages/nftBridge/Header/CopyButton.tsx new file mode 100644 index 000000000..c61f50cb0 --- /dev/null +++ b/src/pages/nftBridge/Header/CopyButton.tsx @@ -0,0 +1,33 @@ +import copy from "copy-to-clipboard" +import { SyntheticEvent, useState } from "react" + +import { Tooltip, Zoom } from "@mui/material" + +import { ReactComponent as CopyIcon } from "@/assets/svgs/copy.svg" +import TextIconButton from "@/components/TextIconButton" + +const CopyButton = (props: any) => { + const { value, ...restProps } = props + const [text, setText] = useState("") + + const handleClick = (event: SyntheticEvent) => { + event.preventDefault() + if (!value) { + return + } + copy(value) + setText("copied!") + setTimeout(() => { + setText("") + }, 3 * 1e3) + } + + return ( + + + + + + ) +} +export default CopyButton diff --git a/src/pages/nftBridge/Header/PageTitle.tsx b/src/pages/nftBridge/Header/PageTitle.tsx new file mode 100644 index 000000000..1ead52a86 --- /dev/null +++ b/src/pages/nftBridge/Header/PageTitle.tsx @@ -0,0 +1,15 @@ +import { Typography } from "@mui/material" +import { Stack } from "@mui/system" + +const PageTitle = () => { + return ( + + NFT Bridge + + Transfer your NFTs across different networks + + + ) +} + +export default PageTitle diff --git a/src/pages/nftBridge/Header/TransactionHistory.tsx b/src/pages/nftBridge/Header/TransactionHistory.tsx new file mode 100644 index 000000000..d8ca0c539 --- /dev/null +++ b/src/pages/nftBridge/Header/TransactionHistory.tsx @@ -0,0 +1,82 @@ +import { makeStyles } from "tss-react/mui" + +import { CircularProgress, Stack, Typography } from "@mui/material" + +import { BRIDGE_PAGE_SIZE } from "@/constants" +import { useApp } from "@/contexts/AppContextProvider" +import useNFTTxStore from "@/stores/nftTxStore" + +import TxTable from "../components/TxTable" + +const useStyles = makeStyles()(theme => { + return { + tableWrapper: { + boxShadow: "unset", + border: `1px solid ${theme.palette.border.main}`, + borderRadius: "1rem", + [theme.breakpoints.down("sm")]: { + border: "unset", + borderRadius: "unset", + margin: "0 -2rem", + width: "calc(100% + 4rem)", + }, + }, + tableTitle: { + marginTop: "2.8rem", + marginBottom: "3rem", + [theme.breakpoints.down("sm")]: { + marginTop: "1.6rem", + marginBottom: "1.6rem", + }, + }, + tableHeader: { + backgroundColor: theme.palette.scaleBackground.primary, + }, + } +}) + +const TransactionsList = (props: any) => { + const { classes, cx } = useStyles() + + const { + txHistory: { refreshPageTransactions }, + } = useApp() + + const { page, total, loading, frontTransactions } = useNFTTxStore() + + // TODO: waiting for api + if (!frontTransactions?.length) { + return ( + + Your transactions will appear here... + + ) + } + + const handleChangePage = currentPage => { + refreshPageTransactions(currentPage) + } + + return ( + <> +
+ + + Recent Bridge Transactions + + {loading && } + +
+ + + ) +} + +export default TransactionsList diff --git a/src/pages/nftBridge/Header/index.tsx b/src/pages/nftBridge/Header/index.tsx new file mode 100644 index 000000000..b1c2e6dc7 --- /dev/null +++ b/src/pages/nftBridge/Header/index.tsx @@ -0,0 +1,143 @@ +import { makeStyles } from "tss-react/mui" + +import CloseIcon from "@mui/icons-material/Close" +import { Box, Card, Container, Divider, Typography } from "@mui/material" +import { Stack } from "@mui/system" + +import { ReactComponent as ExitIcon } from "@/assets/svgs/exit.svg" +import Link from "@/components/Link" +import TextIconButton from "@/components/TextIconButton" +import WalletIndicator from "@/components/WalletIndicator" +// import { useApp } from "@/contexts/AppContextProvider" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useBridgeStore from "@/stores/bridgeStore" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { truncateAddress } from "@/utils" + +import CopyButton from "./CopyButton" +import PageTitle from "./PageTitle" +import TransactionHistory from "./TransactionHistory" + +const useStyles = makeStyles()(theme => ({ + container: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + backgroundColor: theme.palette.background.default, + marginTop: "4rem", + marginBottom: "4rem", + }, + + modalContent: { + width: "max-content", + padding: "2.8rem", + borderRadius: "1rem", + boxSizing: "border-box", + boxShadow: "0px 2px 10px rgba(0, 0, 0, 0.2)", + [theme.breakpoints.down("sm")]: { + width: "calc(100vw - 3.2rem)", + margin: "0 1.6rem", + padding: "2rem", + }, + }, + title: { + [theme.breakpoints.down("sm")]: { + fontSize: "1.8rem", + fontWeight: 600, + }, + }, + + transactionsList: { + margin: " 3rem 0", + [theme.breakpoints.down("sm")]: { + margin: "2.4rem 0", + }, + }, + changeButton: { + position: "absolute", + top: "1rem", + right: "1rem", + borderRadius: "1.5rem", + boxShadow: "none", + }, + disconnectButton: { + position: "absolute", + bottom: "1rem", + right: "1rem", + fontSize: "1.2rem", + marginBottom: 0, + borderRadius: "1.5rem", + boxShadow: "none", + }, + address: { + cursor: "default", + marginRight: "7.2rem", + [theme.breakpoints.down("sm")]: { + marginBottom: "2.4rem", + }, + }, + copyButton: { + marginRight: "2.4rem", + }, +})) + +// TODO: after token minted on L2, need to call setTokenURI for the token (blocked by history) + +const Header = () => { + const { classes } = useStyles() + const { walletCurrentAddress, disconnectWallet } = useWeb3Context() + + const { clearViewingList, clearSelectedList } = useNFTBridgeStore() + + // const { + // txHistory: { refreshPageTransactions }, + // } = useApp() + const { historyVisible, changeHistoryVisible } = useBridgeStore() + + const handleOpen = () => { + changeHistoryVisible(true) + // refreshPageTransactions(1) + } + + const handleClose = () => { + changeHistoryVisible(false) + } + + const handleDisconnect = () => { + handleClose() + clearViewingList() + clearSelectedList() + disconnectWallet() + } + + return ( + + + + + + + Connected Wallet + + + + + + {truncateAddress(walletCurrentAddress as string)} + + + + + + + + + + + + + + ) +} + +export default Header diff --git a/src/pages/nftBridge/NFTPanel/Transfer/ApproveLoadingModal.tsx b/src/pages/nftBridge/NFTPanel/Transfer/ApproveLoadingModal.tsx new file mode 100644 index 000000000..96d0d4ce9 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/ApproveLoadingModal.tsx @@ -0,0 +1,25 @@ +import { Box, Typography } from "@mui/material" + +import Link from "@/components/Link" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" + +import Modal from "../../components/Modal" + +const ApproveLoading = props => { + const { open, onClose } = props + const { walletName } = useWeb3Context() + + return ( + + Not responding? Click here for help + + + Allow Scroll to bridge any your NFTs in the contract + + Approve on your {walletName} wallet + + + ) +} + +export default ApproveLoading diff --git a/src/pages/nftBridge/NFTPanel/Transfer/Fee.tsx b/src/pages/nftBridge/NFTPanel/Transfer/Fee.tsx new file mode 100644 index 000000000..297cc6a83 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/Fee.tsx @@ -0,0 +1,31 @@ +import { useMemo } from "react" + +import { Stack, Typography } from "@mui/material" + +import { ETH_SYMBOL } from "@/constants" +import useGasFee from "@/hooks/useGasFee" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { toTokenDisplay } from "@/utils" + +const Fee = () => { + const { gasFee } = useGasFee() + const { selectedList } = useNFTBridgeStore() + const formattedGasFee = useMemo(() => { + if (!selectedList.length) { + return "-" + } + return toTokenDisplay(gasFee, undefined, ETH_SYMBOL) + }, [gasFee, selectedList]) + return ( + + + Fees + + + {formattedGasFee} + + + ) +} + +export default Fee diff --git a/src/pages/nftBridge/NFTPanel/Transfer/Send.tsx b/src/pages/nftBridge/NFTPanel/Transfer/Send.tsx new file mode 100644 index 000000000..fb15fbcac --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/Send.tsx @@ -0,0 +1,208 @@ +import { useMemo, useState } from "react" + +import { Stack } from "@mui/material" + +import LoadingButton from "@/components/LoadingButton" +import { ChainId, TOEKN_TYPE } from "@/constants" +import { useApp } from "@/contexts/AppContextProvider" +import { useNFTBridgeContext } from "@/contexts/NFTBridgeProvider" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import { useAsyncMemo } from "@/hooks" +import useGasFee from "@/hooks/useGasFee" +import useApprove from "@/hooks/useNFTApprove" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import useNFTTxStore from "@/stores/nftTxStore" + +import ApproveLoadingModal from "./ApproveLoadingModal" +import SendLoadingModal from "./SendLoadingModal" +import TransactionResultModal from "./TransactionResultModal" + +const Send = () => { + const { walletCurrentAddress } = useWeb3Context() + const { networksAndSigners } = useApp() + + const { addNFTTransaction, updateNFTTransaction } = useNFTTxStore() + const { tokenInstance, gatewayAddress, isLayer1 } = useNFTBridgeContext() + const { contract, selectedList, exciseSelected, updatePromptMessage, fromNetwork, toNetwork } = useNFTBridgeStore() + const selectedTokenIds = useNFTBridgeStore(state => state.selectedTokenIds()) + + const { setApproval, checkApproval } = useApprove() + const { gasLimit, gasFee } = useGasFee() + + // button loading + const [sendLoading, setSendLoading] = useState(false) + const [approveLoading, setApproveLoading] = useState(false) + + // modal loading + const [sendModalLoading, setSendModalLoading] = useState(false) + const [approveModalLoading, setApproveModalLoading] = useState(false) + + const [txHash, setTxHash] = useState("") + + // TODO: how to call this after approve + const needApproval = useAsyncMemo(async () => { + if (tokenInstance) { + const isApproved = await checkApproval(tokenInstance, gatewayAddress) + return !isApproved + } + return false + }, [tokenInstance, contract]) + + const sendActive = useMemo(() => { + if (!walletCurrentAddress) { + return false + } else if (!selectedList.length) { + return false + } else if (contract.type === TOEKN_TYPE[1155]) { + const isValid = selectedList.every(item => item.transferAmount && item.transferAmount > 0) + return isValid + } + + return true + }, [walletCurrentAddress, selectedList, contract.type]) + + const handleApprove = async () => { + try { + setApproveLoading(true) + setApproveModalLoading(true) + await setApproval(tokenInstance, gatewayAddress) + } finally { + setApproveLoading(false) + setApproveModalLoading(false) + } + } + + const handleSend = async () => { + setSendLoading(true) + setSendModalLoading(true) + try { + const tx = isLayer1 ? await deposite() : await withdraw() + addNFTTransaction({ + hash: tx.hash, + fromName: fromNetwork.name, + toName: toNetwork.name, + fromExplore: fromNetwork.explorer, + toExplore: toNetwork.explorer, + tokenType: contract.type, + tokenAddress: isLayer1 ? contract.l1 : contract.l2, + amounts: selectedList.map(item => item.transferAmount), + tokenIds: selectedTokenIds, + isL1: isLayer1, + }) + + tx.wait() + .then(receipt => { + updateNFTTransaction(tx.hash, { + fromBlockNumber: receipt.blockNumber, + }) + setTxHash(receipt.transactionHash) + exciseSelected() + }) + .catch(error => { + updatePromptMessage(error.message) + }) + .finally(() => { + setSendLoading(false) + setSendModalLoading(false) + }) + } catch (e) { + setSendLoading(false) + setSendModalLoading(false) + } + } + + const deposite = () => { + if (contract.type === TOEKN_TYPE[721]) { + return deposite721() + } + return deposite1155() + } + + const deposite721 = async () => { + const tx = await networksAndSigners[ChainId.SCROLL_LAYER_1].gateway_721["batchDepositERC721(address,address,uint256[],uint256)"]( + contract.l1, + walletCurrentAddress, + selectedTokenIds, + gasLimit, + { + value: gasFee, + }, + ) + return tx + } + + const deposite1155 = async () => { + const tx = await networksAndSigners[ChainId.SCROLL_LAYER_1].gateway_1155["batchDepositERC1155(address,address,uint256[],uint256[],uint256)"]( + contract.l1, + walletCurrentAddress, + selectedTokenIds, + selectedList.map(item => item.transferAmount), + gasLimit, + { + value: gasFee, + }, + ) + return tx + } + + const withdraw = () => { + if (contract.type === "ERC721") { + return withdraw721() + } + return withdraw1155() + } + + const withdraw721 = async () => { + const tx = await networksAndSigners[ChainId.SCROLL_LAYER_2].gateway_721["batchWithdrawERC721(address,address,uint256[],uint256)"]( + contract.l2, + walletCurrentAddress, + selectedTokenIds, + gasLimit, + { value: gasFee }, + ) + return tx + } + + const withdraw1155 = async () => { + const tx = await networksAndSigners[ChainId.SCROLL_LAYER_2].gateway_1155["batchWithdrawERC1155(address,address,uint256[],uint256[],uint256)"]( + contract.l2, + walletCurrentAddress, + selectedTokenIds, + selectedList.map(item => item.transferAmount), + gasLimit, + { value: gasFee }, + ) + return tx + } + + const handleCloseSendModal = () => { + setSendModalLoading(false) + } + + const handleCloseApproveModal = () => { + setApproveModalLoading(false) + } + + const handleCloseResultModal = () => { + setTxHash("") + } + + return ( + + {needApproval ? ( + + APPROVE + + ) : ( + + SEND + + )} + + + + + ) +} + +export default Send diff --git a/src/pages/nftBridge/NFTPanel/Transfer/SendLoadingModal.tsx b/src/pages/nftBridge/NFTPanel/Transfer/SendLoadingModal.tsx new file mode 100644 index 000000000..bbb6d4dd2 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/SendLoadingModal.tsx @@ -0,0 +1,27 @@ +import { Box, Typography } from "@mui/material" + +import Link from "@/components/Link" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useNFTBridgeStore from "@/stores/nftBridgeStore" + +import Modal from "../../components/Modal" + +const SendLoading = props => { + const { open, onClose } = props + const { walletName } = useWeb3Context() + const { fromNetwork, toNetwork } = useNFTBridgeStore() + + return ( + + Not responding? Click here for help + + + Bridging your NFTs from {fromNetwork.name} to {toNetwork.name} + + Confirm this transaction on your {walletName} wallet + + + ) +} + +export default SendLoading diff --git a/src/pages/nftBridge/NFTPanel/Transfer/TransactionResultModal.tsx b/src/pages/nftBridge/NFTPanel/Transfer/TransactionResultModal.tsx new file mode 100644 index 000000000..303883694 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/TransactionResultModal.tsx @@ -0,0 +1,37 @@ +import { useMemo } from "react" + +import { Button } from "@mui/material" + +import Link from "@/components/Link" +import { BLOCK_EXPLORER } from "@/constants" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import { generateTxLink } from "@/utils" + +import Modal from "../../components/Modal" + +const TransactionResultModal = props => { + const { open, hash, onClose } = props + + const { chainId } = useWeb3Context() + + const txUrl = useMemo(() => { + if (hash && chainId) { + const explorer = BLOCK_EXPLORER[chainId] + return generateTxLink(explorer, hash) + } + return "" + }, [chainId, hash]) + + return ( + + + View on block explorer + + + + ) +} + +export default TransactionResultModal diff --git a/src/pages/nftBridge/NFTPanel/Transfer/index.tsx b/src/pages/nftBridge/NFTPanel/Transfer/index.tsx new file mode 100644 index 000000000..16a943bbb --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Transfer/index.tsx @@ -0,0 +1,42 @@ +import { Box, Divider, Stack, Typography } from "@mui/material" + +import useNFTBridgeStore from "@/stores/nftBridgeStore" + +import Gallery from "../../components/Gallery" +import SelectedItem from "../../components/Gallery/SelectedItem" +import NetworkSelect from "../../components/NetworkSelect" +import Fee from "./Fee" +import Send from "./Send" + +const Transfer = props => { + const { toNetwork, selectedList } = useNFTBridgeStore() + + return ( + + + + Transfer to + + + + + + Selected NFTs + + + {selectedList.map(item => ( + + ))} + + + + + + ) +} + +export default Transfer diff --git a/src/pages/nftBridge/NFTPanel/Viewing/index.tsx b/src/pages/nftBridge/NFTPanel/Viewing/index.tsx new file mode 100644 index 000000000..19d01ad18 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/Viewing/index.tsx @@ -0,0 +1,178 @@ +import { useState } from "react" +import useSWR from "swr" + +import { Box, InputBase, Stack, Typography } from "@mui/material" + +import { nftTokenListUrl } from "@/apis/dynamic" +import { TOEKN_TYPE, networks } from "@/constants" +import { useNFTBridgeContext } from "@/contexts/NFTBridgeProvider" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { requireEnv, switchNetwork } from "@/utils" + +import ContractSelect from "../../components/ContractSelect" +import Gallery from "../../components/Gallery" +import ViewingItem from "../../components/Gallery/ViewingItem" +import NetworkSelect from "../../components/NetworkSelect" +import LoadingButton from "../../components/SearchButton" + +const branchName = requireEnv("REACT_APP_SCROLL_ENVIRONMENT").toLocaleLowerCase() + +const Viewing = props => { + const { walletCurrentAddress } = useWeb3Context() + const { tokenInstance } = useNFTBridgeContext() + const { fromNetwork, viewingList, addViewingList, clearViewingList, clearSelectedList, contract, changeContract, updatePromptMessage } = + useNFTBridgeStore() + + const [currentTokenId, setCurrentTokenId] = useState("") + const [searchLoading, setSearchLoading] = useState(false) + + const { data: contractList } = useSWR( + nftTokenListUrl(branchName), + url => { + return scrollRequest(url) + .then((data: any) => { + if (!contract.type) { + changeContract(data.tokens[0]) + } + return data.tokens + }) + .catch(() => { + // setFetchTokenListError("Fail to fetch token list") + // setTokenSymbol(ETH_SYMBOL) + return [] + }) + }, + { + revalidateOnFocus: false, + }, + ) + const handleChangeFromNetwork = value => { + switchNetwork(value) + clearViewingList() + clearSelectedList() + } + + const handleChangeContract = value => { + changeContract(value) + clearViewingList() + clearSelectedList() + } + + const handleChangeTokenId = e => { + const { value } = e.target + if (value) { + setCurrentTokenId(isNaN(+value) ? "" : +value) + } else { + setCurrentTokenId("") + } + } + + // TODO: tip for not owned/exists token + const handleSearchToken = async () => { + try { + setSearchLoading(true) + if (viewingList.find(item => item.id === currentTokenId)) { + throw new Error("Duplicate TokenId!") + } + let uri + let amount = 1 + if (contract?.type === TOEKN_TYPE[721]) { + const owned = await isOwned(currentTokenId) + + if (owned) { + uri = await tokenInstance["tokenURI(uint256)"](currentTokenId) + } else { + throw new Error("Not your token!") + } + } else { + const exists = await isExists(currentTokenId) + if (exists) { + uri = await tokenInstance["uri(uint256)"](currentTokenId) + amount = await tokenInstance["balanceOf(address,uint256)"](walletCurrentAddress, currentTokenId) + amount = Number(amount) + } else { + throw new Error("Token does not exist!") + } + } + + if (uri && uri.startsWith("http")) { + scrollRequest(uri).then(data => { + addViewingList({ id: currentTokenId, amount, ...data, transferAmount: amount }) + }) + } else { + addViewingList({ id: currentTokenId, amount, name: uri, transferAmount: amount }) + } + setCurrentTokenId("") + } catch (e) { + updatePromptMessage(e.message) + } finally { + setSearchLoading(false) + } + } + + const isOwned = async tokenId => { + try { + const owner = await tokenInstance["ownerOf(uint256)"](tokenId) + return owner === walletCurrentAddress + } catch (e) { + return false + } + } + const isExists = async tokenId => { + try { + const exists = await tokenInstance["exists(uint256)"](tokenId) + return exists + } catch (e) { + return true + } + } + + return ( + + + + Select your NFTs on + + + + + + theme.palette.background.default, + }, + }} + placeholder="TOKEN ID" + value={currentTokenId} + onChange={handleChangeTokenId} + /> + + Search + + + + {viewingList.map(item => ( + + ))} + + + ) +} + +export default Viewing diff --git a/src/pages/nftBridge/NFTPanel/index.tsx b/src/pages/nftBridge/NFTPanel/index.tsx new file mode 100644 index 000000000..47d593b55 --- /dev/null +++ b/src/pages/nftBridge/NFTPanel/index.tsx @@ -0,0 +1,58 @@ +import { useEffect } from "react" + +import { Container } from "@mui/material" + +import { ChainId, networks } from "@/constants" +import NFTBridgeProvider from "@/contexts/NFTBridgeProvider" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { switchNetwork } from "@/utils" + +import CornerTip from "../components/CornerTip" +import Transfer from "./Transfer" +import Viewing from "./Viewing" + +const NFTPanel = () => { + const { chainId } = useWeb3Context() + const { changeFromNetwork, changeToNetwork, promptMessage, updatePromptMessage } = useNFTBridgeStore() + + useEffect(() => { + if (chainId && Object.values(ChainId).includes(chainId)) { + const fromNetworkIndex = networks.findIndex(item => item.chainId === chainId) + changeFromNetwork(networks[fromNetworkIndex]) + changeToNetwork(networks[+!fromNetworkIndex]) + } else if (chainId) { + changeFromNetwork(networks[0]) + changeToNetwork(networks[1]) + switchNetwork(networks[0].chainId) + } else { + changeFromNetwork(networks[0]) + changeToNetwork(networks[1]) + } + }, [chainId]) + + const handleClearpromptMessage = () => { + updatePromptMessage("") + } + return ( + + + + + + {promptMessage} + + + + ) +} + +export default NFTPanel diff --git a/src/pages/nftBridge/components/ContractSelect/index.tsx b/src/pages/nftBridge/components/ContractSelect/index.tsx new file mode 100644 index 000000000..67b265289 --- /dev/null +++ b/src/pages/nftBridge/components/ContractSelect/index.tsx @@ -0,0 +1,113 @@ +import { makeStyles } from "tss-react/mui" + +import { Autocomplete, Chip, Icon, Stack, TextField, Typography } from "@mui/material" + +import { ReactComponent as ArrowDownIcon } from "@/assets/svgs/arrow-down.svg" +import Link from "@/components/Link" +import { ChainId } from "@/constants" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" + +const useStyles = makeStyles()(theme => { + return { + AutocompleteRoot: { + width: "56rem", + color: "#7e7e7e", + }, + listbox: { + padding: 0, + }, + input: { + fontSize: "1.4rem", + height: "1.8rem", + color: "#7e7e7e", + userSelect: "none", + }, + inputRoot: { + backgroundColor: "#e0e0e0", + "&.Mui-focused": { + backgroundColor: theme.palette.background.default, + }, + }, + option: { + paddingLeft: "0.6rem !important", + paddingRight: "0.6rem !important", + justifyContent: "space-between !important", + + ".faucetLink": { + visibility: "hidden", + }, + + "&:hover": { + ".faucetLink": { + visibility: "visible", + }, + }, + }, + + ChipRoot: { + height: "auto", + padding: "0 4px", + borderRadius: "4px", + backgroundColor: "#e0e0e0", + }, + label: { + fontSize: "1rem", + color: "#7e7e7e", + padding: 0, + }, + + clearIndicator: { + transform: "scale(0.6)", + }, + } +}) + +const ContractSelect = props => { + const { value, data, onChange } = props + const { checkConnectedChainId } = useWeb3Context() + const { classes } = useStyles() + return ( + } + size="small" + classes={{ + root: classes.AutocompleteRoot, + listbox: classes.listbox, + input: classes.input, + inputRoot: classes.inputRoot, + option: classes.option, + clearIndicator: classes.clearIndicator, + }} + getOptionLabel={option => (checkConnectedChainId(ChainId.SCROLL_LAYER_1) ? option?.l1 : option?.l2) ?? ""} + renderInput={params => ( + + )} + renderOption={(innerProps: any, option, state) => ( + + + + {checkConnectedChainId(ChainId.SCROLL_LAYER_1) ? option.l1 : option.l2} + + {checkConnectedChainId(ChainId.SCROLL_LAYER_1) && ( + + faucet + + )} + + )} + onChange={(event, newValue) => { + onChange(newValue) + }} + /> + ) +} + +export default ContractSelect diff --git a/src/pages/nftBridge/components/CornerTip/index.tsx b/src/pages/nftBridge/components/CornerTip/index.tsx new file mode 100644 index 000000000..476086234 --- /dev/null +++ b/src/pages/nftBridge/components/CornerTip/index.tsx @@ -0,0 +1,24 @@ +import { makeStyles } from "tss-react/mui" + +import { Alert, Snackbar } from "@mui/material" + +const useStyles = makeStyles()(theme => ({ + message: { + wordBreak: "break-all", + }, +})) + +const CornerTip = props => { + const { open, children, autoHideDuration = 6000, onClose, AlertProps, severity } = props + + const { classes } = useStyles() + return ( + + + {children} + + + ) +} + +export default CornerTip diff --git a/src/pages/nftBridge/components/Gallery/SelectedItem.tsx b/src/pages/nftBridge/components/Gallery/SelectedItem.tsx new file mode 100644 index 000000000..7ffd7299f --- /dev/null +++ b/src/pages/nftBridge/components/Gallery/SelectedItem.tsx @@ -0,0 +1,81 @@ +import { Box, Card, CardContent, CardMedia, IconButton, InputBase, Stack, SvgIcon, Typography } from "@mui/material" + +import { ReactComponent as CloseIconSvg } from "@/assets/svgs/close.svg" +import EmptyImg from "@/assets/svgs/empty-img.svg" +import { TOEKN_TYPE } from "@/constants" +import useNFTBridgeStore from "@/stores/nftBridgeStore" + +const SelectedItem = props => { + const { id, image, name, amount, transferAmount } = props + + const { contract, toggleSelectedList, updateSelectedList } = useNFTBridgeStore() + + const handleRemoveSelectedId = () => { + toggleSelectedList(id) + } + + const handleChangeTransferAmount = e => { + const { value } = e.target + let transferAmount + if (value) { + transferAmount = isNaN(+value) ? undefined : +value + } else { + transferAmount = undefined + } + updateSelectedList(id, { transferAmount }) + } + + return ( + + + + {`#${id}` || name} + {contract.type === TOEKN_TYPE[1155] && ( + + + Amount: + `1px solid ${theme.palette.error.main}`, + }, + }} + value={transferAmount} + onChange={handleChangeTransferAmount} + > + / {amount} + + + )} + + + + + + ) +} + +export default SelectedItem diff --git a/src/pages/nftBridge/components/Gallery/ViewingItem.tsx b/src/pages/nftBridge/components/Gallery/ViewingItem.tsx new file mode 100644 index 000000000..0c90ada0c --- /dev/null +++ b/src/pages/nftBridge/components/Gallery/ViewingItem.tsx @@ -0,0 +1,71 @@ +import { Box, Card, CardContent, CardMedia, Stack, SvgIcon, Typography } from "@mui/material" + +import { ReactComponent as CheckIconSvg } from "@/assets/svgs/check.svg" +import EmptyImg from "@/assets/svgs/empty-img.svg" +import { TOEKN_TYPE } from "@/constants" +import useNFTBridgeStore from "@/stores/nftBridgeStore" + +const GalleryItem = props => { + const { id, image, name, amount } = props + + const { contract, toggleSelectedList } = useNFTBridgeStore() + const selectedTokenIds = useNFTBridgeStore(state => state.selectedTokenIds()) + + const handleToggleSelect = () => { + toggleSelectedList(id) + } + + return ( + + + + {`#${id}` || name} + {contract.type === TOEKN_TYPE[1155] && ( + + + Amount: + {amount} + + + )} + + {selectedTokenIds.includes(id) && ( + + + + )} + + ) +} + +export default GalleryItem diff --git a/src/pages/nftBridge/components/Gallery/index.tsx b/src/pages/nftBridge/components/Gallery/index.tsx new file mode 100644 index 000000000..3dcf5abec --- /dev/null +++ b/src/pages/nftBridge/components/Gallery/index.tsx @@ -0,0 +1,56 @@ +import { Box, Typography } from "@mui/material" +import { styled } from "@mui/material/styles" + +import TextButton from "@/components/TextButton" +import { useWeb3Context } from "@/contexts/Web3ContextProvider" +import useNFTBridgeStore from "@/stores/nftBridgeStore" +import { switchNetwork } from "@/utils" + +const Container = styled("div")( + ({ theme }) => ` + display: grid; + width: 100%; + grid-column-gap: 1rem; + grid-row-gap: 1rem; + grid-auto-rows: min-content; +`, +) + +const SelectedGallery = props => { + const { column, emptyTip, sx, children, ...restProps } = props + const { walletCurrentAddress, connectWallet, chainId } = useWeb3Context() + + const { fromNetwork } = useNFTBridgeStore() + + const renderTip = () => { + if (!walletCurrentAddress) { + return ( + + Click here to connect wallet + + ) + } else if (chainId !== fromNetwork.chainId) { + return switchNetwork(fromNetwork.chainId)}>Click here to switch to {fromNetwork.name}. + } + return ( + + {emptyTip} + + ) + } + return ( + <> + {children.length ? ( + + {children} + + ) : ( + + {renderTip()} + + )} + + ) +} + +export default SelectedGallery diff --git a/src/pages/nftBridge/components/LargeTextField/index.tsx b/src/pages/nftBridge/components/LargeTextField/index.tsx new file mode 100644 index 000000000..7cb03a0a2 --- /dev/null +++ b/src/pages/nftBridge/components/LargeTextField/index.tsx @@ -0,0 +1,73 @@ +import { FC, ReactNode } from "react" +import { makeStyles } from "tss-react/mui" + +import MuiTextField, { StandardTextFieldProps } from "@mui/material/TextField" + +type LargeTextFieldProps = { + units?: string | ReactNode + centerAlign?: boolean | undefined + leftAlign?: boolean | undefined + defaultShadow?: boolean | undefined + smallFontSize?: boolean +} & StandardTextFieldProps + +const useStyles = makeStyles()(theme => { + return { + root: { + display: "flex", + width: "100%", + alignItems: "center", + justifyContent: "flex-end", + }, + adornment: { + width: "auto", + textAlign: "right", + [theme.breakpoints.down("sm")]: { + fontSize: theme.typography.subtitle2.fontSize, + }, + }, + } +}) + +const useInputStyles = makeStyles()((theme, { leftAlign, centerAlign }) => ({ + root: { + transition: "all 0.15s ease-out", + width: "100%", + }, + input: { + textAlign: leftAlign ? "left" : centerAlign ? "center" : "right", + fontSize: theme.typography.h4.fontSize, + fontWeight: theme.typography.h4.fontWeight, + color: theme.palette.text.primary, + textOverflow: "clip", + padding: "6px 4px", + [theme.breakpoints.down("sm")]: { + fontSize: "2.4rem", + }, + }, +})) + +const LargeTextField: FC = props => { + const { className, units, leftAlign = false, centerAlign, sx, ...textFieldProps } = props + const { classes, cx } = useStyles() + const { classes: inputStyles } = useInputStyles({ + leftAlign, + centerAlign, + }) + + return ( + + ) +} + +export default LargeTextField diff --git a/src/pages/nftBridge/components/Modal/index.tsx b/src/pages/nftBridge/components/Modal/index.tsx new file mode 100644 index 000000000..beaf5f5fa --- /dev/null +++ b/src/pages/nftBridge/components/Modal/index.tsx @@ -0,0 +1,51 @@ +import CloseIcon from "@mui/icons-material/Close" +import { CircularProgress, Dialog, DialogContent, DialogTitle, Icon, IconButton, Typography } from "@mui/material" + +import { ReactComponent as SuccessSvg } from "@/assets/svgs/success.svg" + +const Modal = props => { + const { open, title, variant, children, onClose } = props + const renderIndicator = () => { + if (variant === "loading") { + return ( + + ) + } else if (variant === "success") { + return + } + return null + } + + return ( + + + ({ + color: "text.primary", + }), + ]} + onClick={onClose} + > + + + + + {renderIndicator()} + + {title} + + {children} + + + ) +} + +export default Modal diff --git a/src/pages/nftBridge/components/NetworkSelect/index.tsx b/src/pages/nftBridge/components/NetworkSelect/index.tsx new file mode 100644 index 000000000..1246dfe13 --- /dev/null +++ b/src/pages/nftBridge/components/NetworkSelect/index.tsx @@ -0,0 +1,104 @@ +import { makeStyles } from "tss-react/mui" + +import { ListItemIcon, ListItemText, MenuItem, Select } from "@mui/material" + +import { ReactComponent as ArrowDownIcon } from "@/assets/svgs/arrow-down.svg" + +const useStyles = makeStyles()(theme => ({ + networkSelect: { + width: "22rem", + height: "3rem", + border: "none", + borderRadius: "0.8rem", + backgroundColor: theme.palette.background.default, + boxShadow: theme.boxShadows.sharp, + + ".MuiSelect-select": { + display: "flex", + alignItems: "center", + padding: "0 1rem", + + "&:focus": { + backgroundColor: "unset", + }, + "&.Mui-disabled": { + WebkitTextFillColor: theme.palette.text.primary, + }, + }, + ".MuiTypography-root": { + fontSize: "1.3rem", + fontWeight: 500, + }, + ".MuiListItemIcon-root": { + minWidth: "unset", + }, + ".MuiSelect-icon": { + top: "unset", + right: "1.2rem", + color: theme.palette.text.primary, + transform: "scale(0.7)", + }, + ".MuiSelect-iconOpen": { + transform: "rotate(180deg) scale(0.7)", + }, + }, + networkIcon: { + display: "flex", + height: "1.6rem", + margin: "0.4rem", + }, + + networkMenuItem: { + padding: "0.2rem 1rem", + ".MuiTypography-root": { + fontSize: "1.2rem", + }, + ".MuiListItemIcon-root": { + minWidth: "unset", + }, + }, + optionModal: { + borderRadius: "0 0 0.8rem 0.8rem", + }, + optionList: { + padding: 0, + }, + optionListText: { + cursor: "pointer", + }, +})) + +const NetworkSelect = props => { + const { options, value, onChange, ...extraProps } = props + const { classes } = useStyles() + const icon = options.length <= 1 ? () => null : ArrowDownIcon + return ( + + ) +} + +export default NetworkSelect diff --git a/src/pages/nftBridge/components/SearchButton/index.tsx b/src/pages/nftBridge/components/SearchButton/index.tsx new file mode 100644 index 000000000..19fd3affd --- /dev/null +++ b/src/pages/nftBridge/components/SearchButton/index.tsx @@ -0,0 +1,20 @@ +import { styled } from "@mui/system" + +import LoadingButton from "@/components/LoadingButton" + +const SearchButton = styled(LoadingButton)( + ({ theme }: any) => ` + height: 3.5rem; + font-size: 1.4rem; + box-shadow: ${theme.boxShadows.sharp}; + background-color: ${theme.palette.background.default}; + color: ${theme.palette.text.primary}; + :hover{ + color: ${theme.palette.primary.main}; + background-color: ${theme.palette.background.default}; + box-shadow: ${theme.boxShadows.sharp}; + } +`, +) + +export default SearchButton diff --git a/src/pages/nftBridge/components/TokenIdInput/index.tsx b/src/pages/nftBridge/components/TokenIdInput/index.tsx new file mode 100644 index 000000000..9d66a63a9 --- /dev/null +++ b/src/pages/nftBridge/components/TokenIdInput/index.tsx @@ -0,0 +1,27 @@ +import React from "react" + +import SearchIcon from "@mui/icons-material/Search" +import { Divider, IconButton, InputBase, Paper } from "@mui/material" + +const TokenIdInput = props => { + const { value, onChange, onEnsure } = props + + return ( + + ) => { + onChange(event.target.value) + }} + /> + + + + + + ) +} + +export default TokenIdInput diff --git a/src/pages/nftBridge/components/TxTable/index.tsx b/src/pages/nftBridge/components/TxTable/index.tsx new file mode 100644 index 000000000..759d30ec9 --- /dev/null +++ b/src/pages/nftBridge/components/TxTable/index.tsx @@ -0,0 +1,245 @@ +import { useCallback, useMemo } from "react" +import { makeStyles } from "tss-react/mui" + +import { + Chip, + CircularProgress, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from "@mui/material" + +import Link from "@/components/Link" +import { useApp } from "@/contexts/AppContextProvider" +import { generateContractLink, generateTxLink, truncateAddress, truncateHash } from "@/utils" + +const useStyles = makeStyles()(theme => { + return { + tableContainer: { + whiteSpace: "nowrap", + [theme.breakpoints.down("sm")]: { + paddingBottom: "1.6rem", + overflowX: "scroll", + }, + }, + tableWrapper: { + boxShadow: "unset", + border: `1px solid ${theme.palette.border.main}`, + borderRadius: "1rem", + width: "82rem", + }, + tableTitle: { + marginTop: "2.8rem", + marginBottom: "3rem", + [theme.breakpoints.down("sm")]: { + marginTop: "1.6rem", + marginBottom: "1.6rem", + }, + }, + tableHeader: { + backgroundColor: theme.palette.scaleBackground.primary, + ".MuiTableCell-head": { + borderBottom: "unset", + }, + }, + chip: { + width: "9rem", + height: "2.8rem", + fontSize: "1.2rem", + padding: 0, + fontWeight: 500, + ".MuiChip-label": { + paddingLeft: 0, + paddingRight: 0, + }, + }, + pendingChip: { + color: theme.palette.tagWarning.main, + backgroundColor: theme.palette.tagWarning.light, + }, + successChip: { + color: theme.palette.tagSuccess.main, + backgroundColor: theme.palette.tagSuccess.light, + }, + pagination: { + ".MuiPaginationItem-text": { + fontSize: "1.6rem", + }, + ".MuiPaginationItem-root": { + color: theme.palette.text.secondary, + }, + ".MuiPaginationItem-root.Mui-selected": { + fontWeight: 700, + backgroundColor: "unset", + }, + ".MuiSvgIcon-root": { + fontSize: "2.4rem", + }, + }, + } +}) + +const TxTable = (props: any) => { + const { data, pagination, loading } = props + + const { classes } = useStyles() + + const handleChangePage = (e, newPage) => { + pagination?.onChange?.(newPage) + } + + return ( + <> +
+ + + + + Status + Contract Address + Type + Token IDs + Amount + Txn Hash + + + + {loading ? ( + + ) : ( + <> + {data?.map((tx: any) => ( + + ))} + + )} + +
+
+
+ {pagination && ( +
+ +
+ )} + + ) +} + +const TxRow = props => { + const { tx } = props + + const { + txHistory: { blockNumbers }, + } = useApp() + + const { classes, cx } = useStyles() + + const txStatus = useCallback( + (blockNumber, isL1, to) => { + if (!blockNumber || !blockNumbers) { + return "Pending" + } + if (blockNumbers[+!(isL1 ^ to)] >= blockNumber) { + return "Success" + } + return "Pending" + }, + [blockNumbers], + ) + + const fromStatus = useMemo(() => { + return txStatus(tx.fromBlockNumber, tx.isL1, false) + }, [tx, txStatus]) + + const toStatus = useMemo(() => { + return txStatus(tx.toBlockNumber, tx.isL1, true) + }, [tx, txStatus]) + + return ( + + + + {blockNumbers ? ( + <> + + + + ) : ( + <> + + + + )} + + + + + + {truncateAddress(tx.tokenAddress)} + + + + + + + + + + + {tx.tokenIds.map(item => ( + {item} + ))} + + + + + {tx.amounts.map(item => ( + {item} + ))} + + + + + From {tx.fromName}: + + + {truncateHash(tx.hash)} + + + + + + To {tx.toName}: + + {tx.toHash ? ( + + {truncateHash(tx.toHash)} + + ) : ( + - + )} + + + + + ) +} + +export default TxTable diff --git a/src/pages/nftBridge/index.tsx b/src/pages/nftBridge/index.tsx new file mode 100644 index 000000000..7d47a8e9b --- /dev/null +++ b/src/pages/nftBridge/index.tsx @@ -0,0 +1,24 @@ +import { GlobalStyles } from "@mui/material" + +import AppProvider from "@/contexts/AppContextProvider" + +import Header from "./Header" +import NFTPanel from "./NFTPanel" + +const NFTBridge = () => { + return ( + + +
+ + + ) +} + +export default NFTBridge diff --git a/src/routes/index.tsx b/src/routes/index.tsx index c771a8121..0025b394b 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -2,6 +2,7 @@ import IframeEmbedding from "@/components/IframeEmbedding" import Bridge from "@/pages/bridge" import Ecosystem from "@/pages/ecosystem" import Portal from "@/pages/home" +import NFTBridge from "@/pages/nftBridge" import RollupScanBatch from "@/pages/rollup/batch" import RollupScanBlock from "@/pages/rollup/block" import RollupScan from "@/pages/rollup/index" @@ -52,6 +53,13 @@ const routes = [ fullPath: "/bridge", element: , }, + + { + name: "NFTBridge", + path: "/nft-bridge", + fullPath: "/nft-bridge", + element: , + }, { name: "Ecosystem", path: "/ecosystem", diff --git a/src/stores/nftBridgeStore.ts b/src/stores/nftBridgeStore.ts new file mode 100644 index 000000000..9be01c98f --- /dev/null +++ b/src/stores/nftBridgeStore.ts @@ -0,0 +1,134 @@ +import produce from "immer" +import create from "zustand" + +interface NFTToken { + id: number + amount: number + name?: string + image?: string + description?: string + transferAmount?: number +} + +interface Contract { + type?: string + l1?: string + l2?: string + faucet?: string +} + +interface NFTBridgeStore { + fromNetwork: any + toNetwork: any + contract: Contract + viewingList: NFTToken[] + selectedList: NFTToken[] + promptMessage: string + selectedTokenIds: () => number[] + selectedTokenAmount: () => number + changeContract: (value) => void + changeFromNetwork: (value) => void + changeToNetwork: (value) => void + addViewingList: (token) => void + removeViewingList: (id) => void + clearViewingList: () => void + clearSelectedList: () => void + toggleSelectedList: (id) => void + updateSelectedList: (id, params) => void + exciseSelected: () => void + updatePromptMessage: (value) => void +} + +const useNFTBridgeStore = create()((set, get) => ({ + contract: {}, + fromNetwork: { chainId: 0 }, + toNetwork: { chainId: 0 }, + promptMessage: "", + viewingList: [], + selectedList: [], + selectedTokenIds: () => get().selectedList.map(item => item.id), + + selectedTokenAmount: () => get().selectedList.reduce((pre, cur: any) => pre + (cur.transferAmount ?? 0), 0), + + changeFromNetwork: value => { + set({ + fromNetwork: value, + }) + }, + changeToNetwork: value => { + set({ + toNetwork: value, + }) + }, + changeContract: contract => { + set({ + contract: contract || {}, + }) + }, + + addViewingList: token => { + set({ + viewingList: get().viewingList.concat(token), + }) + }, + + removeViewingList: id => { + const curViewingList = get().viewingList + const nextViewingList = curViewingList.filter(item => item.id !== id) + set({ + viewingList: nextViewingList, + }) + }, + + clearViewingList: () => { + set({ + viewingList: [], + }) + }, + + clearSelectedList: () => { + set({ + selectedList: [], + }) + }, + + toggleSelectedList: id => { + const curSelectedList = get().selectedList + const tokenIndex = curSelectedList.findIndex(item => item.id === id) + if (tokenIndex > -1) { + const nextSelectedList = [...curSelectedList] + nextSelectedList.splice(tokenIndex, 1) + set({ + selectedList: nextSelectedList, + }) + } else { + const token = get().viewingList.find(item => item.id === id) + set({ + selectedList: get().selectedList.concat(token as NFTToken), + }) + } + }, + exciseSelected: () => { + const selectedTokenIds = get().selectedTokenIds() + const nextViewingList = get().viewingList.filter(item => !selectedTokenIds.includes(item.id)) + set({ + viewingList: nextViewingList, + selectedList: [], + }) + }, + updateSelectedList: (id, params) => { + set( + produce(state => { + const curToken = state.selectedList.find(item => item.id === id) + for (let key of Object.keys(params)) { + curToken[key] = params[key] + } + }), + ) + }, + updatePromptMessage: promptMessage => { + set({ promptMessage }) + }, +})) + +export default useNFTBridgeStore diff --git a/src/stores/nftTxStore.ts b/src/stores/nftTxStore.ts new file mode 100644 index 000000000..40df86561 --- /dev/null +++ b/src/stores/nftTxStore.ts @@ -0,0 +1,197 @@ +import produce from "immer" +import create from "zustand" +import { persist } from "zustand/middleware" + +import { fetchTxListUrl } from "@/apis/bridge" +import { networks } from "@/constants" +import { NFT_BRIDGE_TRANSACTIONS } from "@/utils/storageKey" + +interface NFTTxStore { + page: number + total: number + loading: boolean + frontTransactions: Transaction[] + transactions: Transaction[] + pageTransactions: Transaction[] + addNFTTransaction: (tx) => void + updateNFTTransaction: (hash, tx) => void + generateNFTTransactions: (transactions, safeBlockNumber) => void + comboPageNFTTransactions: (address, page, rowsPerPage, safeBlockNumber) => Promise + clearNFTTransactions: () => void +} +interface Transaction { + hash: string + toHash?: string + fromName: string + toName: string + fromExplore: string + toExplore?: string + fromBlockNumber?: number + toBlockNumber?: number + tokenType: string + tokenAdress: string + amounts: Array + tokenIds: Array + isL1: boolean +} + +const formatBackTxList = (backList, safeBlockNumber) => { + if (!backList.length) { + return [] + } + return backList.map(tx => { + const amount = tx.amount + const fromName = networks[+!tx.isL1].name + const fromExplore = networks[+!tx.isL1].explorer + const toName = networks[+tx.isL1].name + const toExplore = networks[+tx.isL1].explorer + const toHash = tx.finalizeTx?.hash + const fromEstimatedEndTime = tx.isL1 && tx.blockNumber > safeBlockNumber ? Date.now() + (tx.blockNumber - safeBlockNumber) * 12 * 1000 : undefined + const toEstimatedEndTime = + !tx.isL1 && tx.finalizeTx?.blockNumber && tx.finalizeTx.blockNumber > safeBlockNumber + ? Date.now() + (tx.finalizeTx.blockNumber - safeBlockNumber) * 12 * 1000 + : undefined + return { + hash: tx.hash, + amount, + fromName, + fromExplore, + fromBlockNumber: tx.blockNumber, + fromEstimatedEndTime, + toHash, + toName, + toExplore, + toBlockNumber: tx.finalizeTx?.blockNumber, + isL1: tx.isL1, + symbolToken: tx.isL1 ? tx.l1Token : tx.l2Token, + toEstimatedEndTime, + } + }) +} + +const useNFTTxStore = create()( + persist( + (set, get) => ({ + page: 1, + total: 0, + frontTransactions: [], + loading: false, + // frontTransactions + backendTransactions.slice(0, 2) + transactions: [], + pageTransactions: [], + // when user send a transaction + addNFTTransaction: newTx => + set(state => ({ + frontTransactions: [newTx, ...state.frontTransactions], + transactions: [newTx, ...state.transactions], + })), + // wait transaction success in from network + updateNFTTransaction: (hash, updateOpts) => + set( + produce(state => { + const frontTx = state.frontTransactions.find(item => item.hash === hash) + if (frontTx) { + for (const key in updateOpts) { + frontTx[key] = updateOpts[key] + } + } + // for stay on "recent tx" page + const recentTx = state.transactions.find(item => item.hash === hash) + if (recentTx) { + for (const key in updateOpts) { + recentTx[key] = updateOpts[key] + } + } + // for keep "bridge history" open + const pageTx = state.pageTransactions.find(item => item.hash === hash) + if (pageTx) { + for (const key in updateOpts) { + pageTx[key] = updateOpts[key] + } + } + }), + ), + // polling transactions + // slim frontTransactions and keep the latest 3 backTransactions + generateNFTTransactions: (historyList, safeBlockNumber) => { + const realHistoryList = historyList.filter(item => item) + if (realHistoryList.length) { + const formattedHistoryList = formatBackTxList(realHistoryList, safeBlockNumber) + const formattedHistoryListHash = formattedHistoryList.map(item => item.hash) + const formattedHistoryListMap = Object.fromEntries(formattedHistoryList.map(item => [item.hash, item])) + const pendingFrontList = get().frontTransactions.filter(item => !formattedHistoryListHash.includes(item.hash)) + const pendingFrontListHash = pendingFrontList.map(item => item.hash) + const syncList = formattedHistoryList.filter(item => !pendingFrontListHash.includes(item.hash)) + const restList = get().transactions.filter(item => item.toHash) + + const refreshPageTransaction = get().pageTransactions.map(item => { + if (formattedHistoryListMap[item.hash]) { + return formattedHistoryListMap[item.hash] + } + return item + }) + set({ + transactions: pendingFrontList.concat([...syncList, ...restList].slice(0, 2)), + frontTransactions: pendingFrontList, + pageTransactions: refreshPageTransaction, + }) + } + }, + clearNFTTransactions: () => { + set({ + frontTransactions: [], + transactions: [], + pageTransactions: [], + page: 1, + total: 0, + }) + }, + + // page transactions + comboPageNFTTransactions: async (address, page, rowsPerPage, safeBlockNumber) => { + const frontTransactions = get().frontTransactions + set({ loading: true }) + const offset = (page - 1) * rowsPerPage + // const offset = gap > 0 ? gap : 0; + if (frontTransactions.length >= rowsPerPage + offset) { + set({ + pageTransactions: frontTransactions.slice(offset, offset + rowsPerPage), + page, + loading: false, + }) + return + } + + const currentPageFrontTransactions = frontTransactions.slice((page - 1) * rowsPerPage) + const gap = (page - 1) * rowsPerPage - frontTransactions.length + const relativeOffset = gap > 0 ? gap : 0 + const limit = rowsPerPage - currentPageFrontTransactions.length + + return scrollRequest(`${fetchTxListUrl}?address=${address}&offset=${relativeOffset}&limit=${limit}`) + .then(data => { + set({ + pageTransactions: [...frontTransactions, ...formatBackTxList(data.data.result, safeBlockNumber)], + total: data.data.total, + page, + loading: false, + }) + if (page === 1) { + // keep transactions always frontList + the latest two history list + set({ + transactions: [...frontTransactions, ...formatBackTxList(data.data.result, safeBlockNumber).slice(0, 2)], + }) + } + }) + .catch(error => { + set({ loading: false }) + return Promise.reject(`${error.status}:${error.message}`) + }) + }, + }), + { + name: NFT_BRIDGE_TRANSACTIONS, + }, + ), +) + +export default useNFTTxStore diff --git a/src/theme/light.tsx b/src/theme/light.tsx index e09e852bb..730f58175 100644 --- a/src/theme/light.tsx +++ b/src/theme/light.tsx @@ -98,7 +98,7 @@ const lightTheme = createTheme({ borderColor: paletteOptions.primary.main, color: paletteOptions.primary.main, backgroundColor: paletteOptions.background.default, - width: "21.5rem", + // width: "21.5rem", "&:hover": { backgroundColor: paletteOptions.background.default, borderColor: paletteOptions.primary.main, diff --git a/src/theme/options.ts b/src/theme/options.ts index eec9db680..2a3b52385 100644 --- a/src/theme/options.ts +++ b/src/theme/options.ts @@ -172,11 +172,6 @@ export const typographyOptions = { fontSize: "1.4rem", lineHeight: "2.6rem", }, - // button: { - // fontSize: "1.8rem", - // fontWeight: 700, - // textTransform: "capitalize", - // }, } export const boxShadowOptions = { diff --git a/src/utils/common.ts b/src/utils/common.ts index 7a6b90865..56f89ddab 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -27,10 +27,14 @@ export function requireEnv(entry) { } } -export const generateExploreLink = (explorer, hash) => { +export const generateTxLink = (explorer, hash) => { return `${explorer}/tx/${hash}` } +export const generateContractLink = (explorer, address) => { + return `${explorer}/address/${address}` +} + export const isProduction = requireEnv("REACT_APP_SCROLL_ENVIRONMENT") === requireEnv("REACT_APP_MAIN_ENVIRONMENT") export const isValidEmail = (email: string): boolean => { diff --git a/src/utils/storageKey.ts b/src/utils/storageKey.ts index 232d06866..eb287dec5 100644 --- a/src/utils/storageKey.ts +++ b/src/utils/storageKey.ts @@ -9,6 +9,8 @@ export const BRIDGE_TOKEN_SYMBOL = "bridgeTokenSymbol" export const BRIDGE_TRANSACTIONS = "bridgeTransactions" +export const NFT_BRIDGE_TRANSACTIONS = "nftBridgeTransactions" + export const APP_VERSION = "appVersion" export const BLOCK_NUMBERS = "blockNumbers" diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index b55abf747..8408be586 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -265,7 +265,7 @@ const Home = () => { Scroll is a team of passionate contributors around the globe. We value ideas and execution above all else. Join us today! -