diff --git a/artifacts/ChildMintableERC721.json b/artifacts/ChildMintableERC721.json index e0989a64..bff1e644 100644 --- a/artifacts/ChildMintableERC721.json +++ b/artifacts/ChildMintableERC721.json @@ -1 +1 @@ -{"abi":[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"childChainManager","type":"address"}],"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":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","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":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDomainSeperator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","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":"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":"uint256","name":"","type":"uint256"}],"name":"withdrawnTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"}]} +{"abi":[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"childChainManager","type":"address"}],"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":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","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"},{"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"},{"indexed":false,"internalType":"bytes","name":"metaData","type":"bytes"}],"name":"TransferWithMetadata","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDomainSeperator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","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":"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":"uint256","name":"","type":"uint256"}],"name":"withdrawnTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawWithMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"encodeTokenMetadata","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"}]} diff --git a/artifacts/DummyMintableERC721.json b/artifacts/DummyMintableERC721.json index d8dc4b84..a798318e 100644 --- a/artifacts/DummyMintableERC721.json +++ b/artifacts/DummyMintableERC721.json @@ -1 +1 @@ -{"abi":[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","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":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PREDICATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDomainSeperator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","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":"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":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]} +{"abi":[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","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":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PREDICATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getDomainSeperator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","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":"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":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"metaData","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]} diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol index 96efbe11..73a2d267 100644 --- a/contracts/Migrations.sol +++ b/contracts/Migrations.sol @@ -1,5 +1,6 @@ pragma solidity 0.6.6; + contract Migrations { address public owner; uint256 public last_completed_migration; diff --git a/contracts/child/ChildChainManager/ChildChainManager.sol b/contracts/child/ChildChainManager/ChildChainManager.sol index e3240368..317138eb 100644 --- a/contracts/child/ChildChainManager/ChildChainManager.sol +++ b/contracts/child/ChildChainManager/ChildChainManager.sol @@ -7,6 +7,7 @@ import {Initializable} from "../../common/Initializable.sol"; import {AccessControlMixin} from "../../common/AccessControlMixin.sol"; import {IStateReceiver} from "../IStateReceiver.sol"; + contract ChildChainManager is IChildChainManager, Initializable, @@ -76,16 +77,19 @@ contract ChildChainManager is } } - function _syncDeposit(bytes memory syncData) private { - (address user, address rootToken, bytes memory depositData) = abi - .decode(syncData, (address, address, bytes)); - address childTokenAddress = rootToChildToken[rootToken]; - require( - childTokenAddress != address(0x0), - "ChildChainManager: TOKEN_NOT_MAPPED" - ); - IChildToken childTokenContract = IChildToken(childTokenAddress); - childTokenContract.deposit(user, depositData); + /** + * @notice Clean polluted token mapping + * @param rootToken address of token on root chain. Since rename token was introduced later stage, + * clean method is used to clean pollulated mapping + */ + function cleanMapToken( + address rootToken, + address childToken + ) external override only(MAPPER_ROLE) { + rootToChildToken[rootToken] = address(0); + childToRootToken[childToken] = address(0); + + emit TokenMapped(rootToken, childToken); } function _mapToken(address rootToken, address childToken) private { @@ -106,18 +110,15 @@ contract ChildChainManager is emit TokenMapped(rootToken, childToken); } - /** - * @notice Clean polluted token mapping - * @param rootToken address of token on root chain. Since rename token was introduced later stage, - * clean method is used to clean pollulated mapping - */ - function cleanMapToken( - address rootToken, - address childToken - ) external override only(MAPPER_ROLE) { - rootToChildToken[rootToken] = address(0); - childToRootToken[childToken] = address(0); - - emit TokenMapped(rootToken, childToken); + function _syncDeposit(bytes memory syncData) private { + (address user, address rootToken, bytes memory depositData) = abi + .decode(syncData, (address, address, bytes)); + address childTokenAddress = rootToChildToken[rootToken]; + require( + childTokenAddress != address(0x0), + "ChildChainManager: TOKEN_NOT_MAPPED" + ); + IChildToken childTokenContract = IChildToken(childTokenAddress); + childTokenContract.deposit(user, depositData); } } diff --git a/contracts/child/ChildChainManager/ChildChainManagerProxy.sol b/contracts/child/ChildChainManager/ChildChainManagerProxy.sol index 99de0fe4..e0471a8c 100644 --- a/contracts/child/ChildChainManager/ChildChainManagerProxy.sol +++ b/contracts/child/ChildChainManager/ChildChainManagerProxy.sol @@ -2,6 +2,7 @@ pragma solidity 0.6.6; import {UpgradableProxy} from "../../common/Proxy/UpgradableProxy.sol"; + contract ChildChainManagerProxy is UpgradableProxy { constructor(address _proxyTo) public diff --git a/contracts/child/ChildToken/ChildMintableERC721.sol b/contracts/child/ChildToken/ChildMintableERC721.sol index 5925f43f..1572fec8 100644 --- a/contracts/child/ChildToken/ChildMintableERC721.sol +++ b/contracts/child/ChildToken/ChildMintableERC721.sol @@ -17,6 +17,8 @@ contract ChildMintableERC721 is bytes32 public constant DEPOSITOR_ROLE = keccak256("DEPOSITOR_ROLE"); mapping (uint256 => bool) public withdrawnTokens; + event TransferWithMetadata(address indexed from, address indexed to, uint256 indexed tokenId, bytes metaData); + constructor( string memory name_, string memory symbol_, @@ -71,6 +73,47 @@ contract ChildMintableERC721 is _burn(tokenId); } + /** + * @notice called when user wants to withdraw token back to root chain with token URI + * @dev Should handle withraw by burning user's token. + * Should set `withdrawnTokens` mapping to `true` for the tokenId being withdrawn + * This transaction will be verified when exiting on root chain + * + * Before calling this function, you may want calling `encodeTokenMetadata` + * and get metadata to be transferred from L2 to L1 during exit + * + * @param tokenId tokenId to withdraw + */ + function withdrawWithMetadata(uint256 tokenId) external { + + require(_msgSender() == ownerOf(tokenId), "ChildMintableERC721: INVALID_TOKEN_OWNER"); + withdrawnTokens[tokenId] = true; + + // Encoding metadata associated with tokenId & emitting event + emit TransferWithMetadata(ownerOf(tokenId), address(0), tokenId, this.encodeTokenMetadata(tokenId)); + + _burn(tokenId); + + } + + /** + * @notice This method is supposed to be called by client when withdrawing token with metadata + * and pass return value of this function as second paramter of `withdrawWithMetadata` method + * + * It can be overridden by clients to encode data in a different form, which needs to + * be decoded back by them correctly during exiting + * + * @param tokenId Token for which URI to be fetched + */ + function encodeTokenMetadata(uint256 tokenId) external view virtual returns (bytes memory) { + + // You're always free to change this default implementation + // and pack more data in byte array which can be decoded back + // in L1 + return abi.encode(tokenURI(tokenId)); + + } + /** * @notice Example function to handle minting tokens on matic chain * @dev Minting can be done as per requirement, diff --git a/contracts/root/RootToken/DummyMintableERC721.sol b/contracts/root/RootToken/DummyMintableERC721.sol index d3742362..19d1abb5 100644 --- a/contracts/root/RootToken/DummyMintableERC721.sol +++ b/contracts/root/RootToken/DummyMintableERC721.sol @@ -40,6 +40,36 @@ contract DummyMintableERC721 is _mint(user, tokenId); } + /** + * If you're attempting to bring metadata associated with token + * from L2 to L1, you must implement this method, to be invoked + * when minting token back on L1, during exit + */ + function setTokenMetadata(uint256 tokenId, bytes memory data) internal virtual { + // This function should decode metadata obtained from L2 + // and attempt to set it for this `tokenId` + // + // Following is just a default implementation, feel + // free to define your own encoding/ decoding scheme + // for L2 -> L1 token metadata transfer + string memory uri = abi.decode(data, (string)); + + _setTokenURI(tokenId, uri); + } + + /** + * @dev See {IMintableERC721-mint}. + * + * If you're attempting to bring metadata associated with token + * from L2 to L1, you must implement this method + */ + function mint(address user, uint256 tokenId, bytes calldata metaData) external override only(PREDICATE_ROLE) { + _mint(user, tokenId); + + setTokenMetadata(tokenId, metaData); + } + + /** * @dev See {IMintableERC721-exists}. */ diff --git a/contracts/root/RootToken/IMintableERC721.sol b/contracts/root/RootToken/IMintableERC721.sol index 4e9db71d..b150d710 100644 --- a/contracts/root/RootToken/IMintableERC721.sol +++ b/contracts/root/RootToken/IMintableERC721.sol @@ -12,6 +12,19 @@ interface IMintableERC721 is IERC721 { */ function mint(address user, uint256 tokenId) external; + /** + * @notice called by predicate contract to mint tokens while withdrawing with metadata from L2 + * @dev Should be callable only by MintableERC721Predicate + * Make sure minting is only done either by this function/ 👆 + * @param user user address for whom token is being minted + * @param tokenId tokenId being minted + * @param metaData Associated token metadata, to be decoded & set using `setTokenMetadata` + * + * Note : If you're interested in taking token metadata from L2 to L1 during exit, you must + * implement this method + */ + function mint(address user, uint256 tokenId, bytes calldata metaData) external; + /** * @notice check if token already exists, return true if it does exist * @dev this check will be used by the predicate to determine if the token needs to be minted or transfered diff --git a/contracts/root/TokenPredicates/MintableERC721Predicate.sol b/contracts/root/TokenPredicates/MintableERC721Predicate.sol index 01e7bb8a..f373da73 100644 --- a/contracts/root/TokenPredicates/MintableERC721Predicate.sol +++ b/contracts/root/TokenPredicates/MintableERC721Predicate.sol @@ -17,6 +17,8 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial bytes32 public constant TOKEN_TYPE = 0xd4392723c111fcb98b073fe55873efb447bcd23cd3e49ec9ea2581930cd01ddc; // keccak("Transfer(address,address,uint256)") bytes32 public constant TRANSFER_EVENT_SIG = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; + // keccak("TransferWithMetadata(address,address,uint256,string)") + bytes32 public constant TRANSFER_WITH_METADATA_EVENT_SIG = 0xf3c6803764de9a0fc1c2acb6a71f53407c5e2b9a3e04973e6586da23d64ecaa5; event LockedMintableERC721( address indexed depositor, @@ -92,28 +94,69 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList(); RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics - require( - bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG, // topic0 is event sig - "MintableERC721Predicate: INVALID_SIGNATURE" - ); - - address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address - - require( - address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address - "MintableERC721Predicate: INVALID_RECEIVER" - ); - - IMintableERC721 token = IMintableERC721(rootToken); - uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field - if (token.exists(tokenId)) { - token.safeTransferFrom( - address(this), - withdrawer, - tokenId - ); + // If it's a simple exit ( with out metadata coming from L2 to L1 ) + if(bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG) { + + address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address + + require( + address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address + "MintableERC721Predicate: INVALID_RECEIVER" + ); + + IMintableERC721 token = IMintableERC721(rootToken); + + uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field + if (token.exists(tokenId)) { + token.safeTransferFrom( + address(this), + withdrawer, + tokenId + ); + } else { + token.mint(withdrawer, tokenId); + } + + } else if (bytes32(logTopicRLPList[0].toUint()) == TRANSFER_WITH_METADATA_EVENT_SIG) { + // If this is NFT exit with metadata i.e. URI 👆 + // + // Note: If your token is only minted in L2, you can exit + // it with metadata. But if it was minted on L1, it'll be + // simply transferred to withdrawer address. And in that case, + // it's lot better to exit with `Transfer(address,address,uint256)` + // i.e. calling `withdraw` method on L2 contract + // event signature proof, which is defined under first `if` clause + // + // If you've called `withdrawWithMetadata`, you should submit + // proof of event signature `TransferWithMetadata(address,address,uint256,bytes)` + + address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address + + require( + address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address + "MintableERC721Predicate: INVALID_RECEIVER" + ); + + IMintableERC721 token = IMintableERC721(rootToken); + + uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field + if (token.exists(tokenId)) { + token.safeTransferFrom( + address(this), + withdrawer, + tokenId + ); + } else { + // Minting with metadata received from L2 i.e. emitted + // by event `TransferWithMetadata` during burning + token.mint(withdrawer, tokenId, logRLPList[2].toBytes()); + } + } else { - token.mint(withdrawer, tokenId); + // Attempting to exit with some event signature from L2, which is + // not ( yet ) supported by L1 exit manager + revert("MintableERC721Predicate: INVALID_SIGNATURE"); } + } } diff --git a/flat/ChildChainManager.sol b/flat/ChildChainManager.sol index 5e75553a..8ad4de17 100644 --- a/flat/ChildChainManager.sol +++ b/flat/ChildChainManager.sol @@ -786,6 +786,7 @@ pragma solidity 0.6.6; + contract ChildChainManager is IChildChainManager, Initializable, @@ -855,16 +856,19 @@ contract ChildChainManager is } } - function _syncDeposit(bytes memory syncData) private { - (address user, address rootToken, bytes memory depositData) = abi - .decode(syncData, (address, address, bytes)); - address childTokenAddress = rootToChildToken[rootToken]; - require( - childTokenAddress != address(0x0), - "ChildChainManager: TOKEN_NOT_MAPPED" - ); - IChildToken childTokenContract = IChildToken(childTokenAddress); - childTokenContract.deposit(user, depositData); + /** + * @notice Clean polluted token mapping + * @param rootToken address of token on root chain. Since rename token was introduced later stage, + * clean method is used to clean pollulated mapping + */ + function cleanMapToken( + address rootToken, + address childToken + ) external override only(MAPPER_ROLE) { + rootToChildToken[rootToken] = address(0); + childToRootToken[childToken] = address(0); + + emit TokenMapped(rootToken, childToken); } function _mapToken(address rootToken, address childToken) private { @@ -885,18 +889,15 @@ contract ChildChainManager is emit TokenMapped(rootToken, childToken); } - /** - * @notice Clean polluted token mapping - * @param rootToken address of token on root chain. Since rename token was introduced later stage, - * clean method is used to clean pollulated mapping - */ - function cleanMapToken( - address rootToken, - address childToken - ) external override only(MAPPER_ROLE) { - rootToChildToken[rootToken] = address(0); - childToRootToken[childToken] = address(0); - - emit TokenMapped(rootToken, childToken); + function _syncDeposit(bytes memory syncData) private { + (address user, address rootToken, bytes memory depositData) = abi + .decode(syncData, (address, address, bytes)); + address childTokenAddress = rootToChildToken[rootToken]; + require( + childTokenAddress != address(0x0), + "ChildChainManager: TOKEN_NOT_MAPPED" + ); + IChildToken childTokenContract = IChildToken(childTokenAddress); + childTokenContract.deposit(user, depositData); } } diff --git a/flat/ChildChainManagerProxy.sol b/flat/ChildChainManagerProxy.sol index efd2b5f4..25499fc7 100644 --- a/flat/ChildChainManagerProxy.sol +++ b/flat/ChildChainManagerProxy.sol @@ -161,6 +161,7 @@ contract UpgradableProxy is Proxy { pragma solidity 0.6.6; + contract ChildChainManagerProxy is UpgradableProxy { constructor(address _proxyTo) public diff --git a/flat/ChildMintableERC721.sol b/flat/ChildMintableERC721.sol index 80a93710..651d2108 100644 --- a/flat/ChildMintableERC721.sol +++ b/flat/ChildMintableERC721.sol @@ -2120,6 +2120,8 @@ contract ChildMintableERC721 is bytes32 public constant DEPOSITOR_ROLE = keccak256("DEPOSITOR_ROLE"); mapping (uint256 => bool) public withdrawnTokens; + event TransferWithMetadata(address indexed from, address indexed to, uint256 indexed tokenId, bytes metaData); + constructor( string memory name_, string memory symbol_, @@ -2174,6 +2176,47 @@ contract ChildMintableERC721 is _burn(tokenId); } + /** + * @notice called when user wants to withdraw token back to root chain with token URI + * @dev Should handle withraw by burning user's token. + * Should set `withdrawnTokens` mapping to `true` for the tokenId being withdrawn + * This transaction will be verified when exiting on root chain + * + * Before calling this function, you may want calling `encodeTokenMetadata` + * and get metadata to be transferred from L2 to L1 during exit + * + * @param tokenId tokenId to withdraw + */ + function withdrawWithMetadata(uint256 tokenId) external { + + require(_msgSender() == ownerOf(tokenId), "ChildMintableERC721: INVALID_TOKEN_OWNER"); + withdrawnTokens[tokenId] = true; + + // Encoding metadata associated with tokenId & emitting event + emit TransferWithMetadata(ownerOf(tokenId), address(0), tokenId, this.encodeTokenMetadata(tokenId)); + + _burn(tokenId); + + } + + /** + * @notice This method is supposed to be called by client when withdrawing token with metadata + * and pass return value of this function as second paramter of `withdrawWithMetadata` method + * + * It can be overridden by clients to encode data in a different form, which needs to + * be decoded back by them correctly during exiting + * + * @param tokenId Token for which URI to be fetched + */ + function encodeTokenMetadata(uint256 tokenId) external view virtual returns (bytes memory) { + + // You're always free to change this default implementation + // and pack more data in byte array which can be decoded back + // in L1 + return abi.encode(tokenURI(tokenId)); + + } + /** * @notice Example function to handle minting tokens on matic chain * @dev Minting can be done as per requirement, diff --git a/flat/DummyMintableERC721.sol b/flat/DummyMintableERC721.sol index a878275c..36072d26 100644 --- a/flat/DummyMintableERC721.sol +++ b/flat/DummyMintableERC721.sol @@ -2079,6 +2079,19 @@ interface IMintableERC721 is IERC721 { */ function mint(address user, uint256 tokenId) external; + /** + * @notice called by predicate contract to mint tokens while withdrawing with metadata from L2 + * @dev Should be callable only by MintableERC721Predicate + * Make sure minting is only done either by this function/ 👆 + * @param user user address for whom token is being minted + * @param tokenId tokenId being minted + * @param metaData Associated token metadata, to be decoded & set using `setTokenMetadata` + * + * Note : If you're interested in taking token metadata from L2 to L1 during exit, you must + * implement this method + */ + function mint(address user, uint256 tokenId, bytes calldata metaData) external; + /** * @notice check if token already exists, return true if it does exist * @dev this check will be used by the predicate to determine if the token needs to be minted or transfered @@ -2157,6 +2170,36 @@ contract DummyMintableERC721 is _mint(user, tokenId); } + /** + * If you're attempting to bring metadata associated with token + * from L2 to L1, you must implement this method, to be invoked + * when minting token back on L1, during exit + */ + function setTokenMetadata(uint256 tokenId, bytes memory data) internal virtual { + // This function should decode metadata obtained from L2 + // and attempt to set it for this `tokenId` + // + // Following is just a default implementation, feel + // free to define your own encoding/ decoding scheme + // for L2 -> L1 token metadata transfer + string memory uri = abi.decode(data, (string)); + + _setTokenURI(tokenId, uri); + } + + /** + * @dev See {IMintableERC721-mint}. + * + * If you're attempting to bring metadata associated with token + * from L2 to L1, you must implement this method + */ + function mint(address user, uint256 tokenId, bytes calldata metaData) external override only(PREDICATE_ROLE) { + _mint(user, tokenId); + + setTokenMetadata(tokenId, metaData); + } + + /** * @dev See {IMintableERC721-exists}. */ diff --git a/flat/MintableERC721Predicate.sol b/flat/MintableERC721Predicate.sol index 1e20004e..4f0ad4f9 100644 --- a/flat/MintableERC721Predicate.sol +++ b/flat/MintableERC721Predicate.sol @@ -1117,6 +1117,19 @@ interface IMintableERC721 is IERC721 { */ function mint(address user, uint256 tokenId) external; + /** + * @notice called by predicate contract to mint tokens while withdrawing with metadata from L2 + * @dev Should be callable only by MintableERC721Predicate + * Make sure minting is only done either by this function/ 👆 + * @param user user address for whom token is being minted + * @param tokenId tokenId being minted + * @param metaData Associated token metadata, to be decoded & set using `setTokenMetadata` + * + * Note : If you're interested in taking token metadata from L2 to L1 during exit, you must + * implement this method + */ + function mint(address user, uint256 tokenId, bytes calldata metaData) external; + /** * @notice check if token already exists, return true if it does exist * @dev this check will be used by the predicate to determine if the token needs to be minted or transfered @@ -1198,6 +1211,8 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial bytes32 public constant TOKEN_TYPE = 0xd4392723c111fcb98b073fe55873efb447bcd23cd3e49ec9ea2581930cd01ddc; // keccak("Transfer(address,address,uint256)") bytes32 public constant TRANSFER_EVENT_SIG = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; + // keccak("TransferWithMetadata(address,address,uint256,string)") + bytes32 public constant TRANSFER_WITH_METADATA_EVENT_SIG = 0xf3c6803764de9a0fc1c2acb6a71f53407c5e2b9a3e04973e6586da23d64ecaa5; event LockedMintableERC721( address indexed depositor, @@ -1273,28 +1288,69 @@ contract MintableERC721Predicate is ITokenPredicate, AccessControlMixin, Initial RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList(); RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics - require( - bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG, // topic0 is event sig - "MintableERC721Predicate: INVALID_SIGNATURE" - ); + // If it's a simple exit ( with out metadata coming from L2 to L1 ) + if(bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG) { - address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address + address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address - require( - address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address - "MintableERC721Predicate: INVALID_RECEIVER" - ); + require( + address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address + "MintableERC721Predicate: INVALID_RECEIVER" + ); + + IMintableERC721 token = IMintableERC721(rootToken); + + uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field + if (token.exists(tokenId)) { + token.safeTransferFrom( + address(this), + withdrawer, + tokenId + ); + } else { + token.mint(withdrawer, tokenId); + } + + } else if (bytes32(logTopicRLPList[0].toUint()) == TRANSFER_WITH_METADATA_EVENT_SIG) { + // If this is NFT exit with metadata i.e. URI 👆 + // + // Note: If your token is only minted in L2, you can exit + // it with metadata. But if it was minted on L1, it'll be + // simply transferred to withdrawer address. And in that case, + // it's lot better to exit with `Transfer(address,address,uint256)` + // i.e. calling `withdraw` method on L2 contract + // event signature proof, which is defined under first `if` clause + // + // If you've called `withdrawWithMetadata`, you should submit + // proof of event signature `TransferWithMetadata(address,address,uint256,bytes)` + + address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address + + require( + address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address + "MintableERC721Predicate: INVALID_RECEIVER" + ); + + IMintableERC721 token = IMintableERC721(rootToken); + + uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field + if (token.exists(tokenId)) { + token.safeTransferFrom( + address(this), + withdrawer, + tokenId + ); + } else { + // Minting with metadata received from L2 i.e. emitted + // by event `TransferWithMetadata` during burning + token.mint(withdrawer, tokenId, logRLPList[2].toBytes()); + } - IMintableERC721 token = IMintableERC721(rootToken); - uint256 tokenId = logTopicRLPList[3].toUint(); // topic3 is tokenId field - if (token.exists(tokenId)) { - token.safeTransferFrom( - address(this), - withdrawer, - tokenId - ); } else { - token.mint(withdrawer, tokenId); + // Attempting to exit with some event signature from L2, which is + // not ( yet ) supported by L1 exit manager + revert("MintableERC721Predicate: INVALID_SIGNATURE"); } + } } diff --git a/test/helpers/constants.js b/test/helpers/constants.js index 7f9f2ae7..206468c2 100644 --- a/test/helpers/constants.js +++ b/test/helpers/constants.js @@ -17,6 +17,7 @@ export const etherAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const erc20TransferEventSig = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' export const erc721TransferEventSig = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' +export const erc721TransferWithMetadataEventSig = '0xf3c6803764de9a0fc1c2acb6a71f53407c5e2b9a3e04973e6586da23d64ecaa5' export const erc1155TransferSingleEventSig = '0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62' export const erc1155TransferBatchEventSig = '0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb' diff --git a/test/helpers/logs.js b/test/helpers/logs.js index badf6684..466a77fe 100644 --- a/test/helpers/logs.js +++ b/test/helpers/logs.js @@ -1,10 +1,10 @@ import { RLP } from 'ethers/utils' import { AbiCoder } from 'ethers/utils' - import { erc20TransferEventSig, erc721TransferEventSig, + erc721TransferWithMetadataEventSig, erc1155TransferSingleEventSig, erc1155TransferBatchEventSig } from './constants' @@ -45,6 +45,25 @@ export const getERC721TransferLog = ({ ]) } +export const getERC721TransferWithMetadataLog = ({ + overrideSig, + from, + to, + tokenId, + metaData +}) => { + return RLP.encode([ + '0x0', + [ + overrideSig || erc721TransferWithMetadataEventSig, + from, + to, + '0x' + tokenId.toString(16) + ], + abi.encode(['string'], [metaData]) // ABI encoded metadata, because that's how dummy root token expects it + ]) +} + export const getERC1155TransferSingleLog = ({ overrideSig, operator, diff --git a/test/predicates/MintableERC721Predicate.test.js b/test/predicates/MintableERC721Predicate.test.js index ffde75d7..a9266fd6 100644 --- a/test/predicates/MintableERC721Predicate.test.js +++ b/test/predicates/MintableERC721Predicate.test.js @@ -8,7 +8,7 @@ import { expectRevert } from '@openzeppelin/test-helpers' import * as deployer from '../helpers/deployer' import { mockValues } from '../helpers/constants' import logDecoder from '../helpers/log-decoder.js' -import { getERC721TransferLog } from '../helpers/logs' +import { getERC721TransferLog, getERC721TransferWithMetadataLog } from '../helpers/logs' // Enable and inject BN dependency chai @@ -258,6 +258,38 @@ contract('MintableERC721Predicate', (accounts) => { }) }) + describe('exitTokens with `TransferWithMetadata` event signature', () => { + const tokenId = mockValues.numbers[5] + const withdrawer = mockValues.addresses[8] + let dummyMintableERC721 + let mintableERC721Predicate + + before(async() => { + const contracts = await deployer.deployFreshRootContracts(accounts) + dummyMintableERC721 = contracts.dummyMintableERC721 + mintableERC721Predicate = contracts.mintableERC721Predicate + const PREDICATE_ROLE = await dummyMintableERC721.PREDICATE_ROLE() + await dummyMintableERC721.grantRole(PREDICATE_ROLE, mintableERC721Predicate.address) + }) + + it('Transaction should go through', async() => { + const burnLog = getERC721TransferWithMetadataLog({ + from: withdrawer, + to: mockValues.zeroAddress, + tokenId, + metaData: 'https://nft.matic.network' + }) + let exitTx = await mintableERC721Predicate.exitTokens(withdrawer, dummyMintableERC721.address, burnLog) + should.exist(exitTx) + }) + + it('Token should be transfered to withdrawer', async() => { + const owner = await dummyMintableERC721.ownerOf(tokenId) + owner.should.equal(withdrawer) + }) + + }) + describe('exitTokens with incorrect burn transaction signature', () => { const tokenId = mockValues.numbers[5] const withdrawer = mockValues.addresses[8]