{"id":27330081,"url":"https://github.com/ccniuj/optimistic-rollup-example-erc721","last_synced_at":"2025-08-10T07:05:40.423Z","repository":{"id":84316453,"uuid":"374605977","full_name":"ccniuj/optimistic-rollup-example-erc721","owner":"ccniuj","description":"Optimistic Rollup Example: ERC721","archived":false,"fork":false,"pushed_at":"2021-06-11T08:06:33.000Z","size":141,"stargazers_count":10,"open_issues_count":2,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T13:02:58.022Z","etag":null,"topics":["optimism","optimism-tutorial","optimistic-rollup"],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ccniuj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-06-07T09:19:42.000Z","updated_at":"2024-04-23T07:26:57.000Z","dependencies_parsed_at":"2023-03-12T18:00:36.317Z","dependency_job_id":null,"html_url":"https://github.com/ccniuj/optimistic-rollup-example-erc721","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ccniuj/optimistic-rollup-example-erc721","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccniuj%2Foptimistic-rollup-example-erc721","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccniuj%2Foptimistic-rollup-example-erc721/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccniuj%2Foptimistic-rollup-example-erc721/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccniuj%2Foptimistic-rollup-example-erc721/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ccniuj","download_url":"https://codeload.github.com/ccniuj/optimistic-rollup-example-erc721/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccniuj%2Foptimistic-rollup-example-erc721/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269688151,"owners_count":24459410,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["optimism","optimism-tutorial","optimistic-rollup"],"created_at":"2025-04-12T12:59:47.875Z","updated_at":"2025-08-10T07:05:40.414Z","avatar_url":"https://github.com/ccniuj.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Optimistic Rollup Example: ERC721\n\n![](https://i.imgur.com/jJKCZ84.png)\n\n## Introduction\n\nThis example demonstrates how to deploy and interact with ERC721 contract using Optimistic Rollup, including the following operations:\n- **Deposit** ERC721 from L1 to L2\n- **Transfer** ERC721 in L2\n- **Withdraw** ERC721 from L2 to L1\n\nHere are three other great examples:\n- [Optimistic Rollup Example: ERC20](https://github.com/ccniuj/optimistic-rollup-example-erc20)\n- [optimism-tutorial](https://github.com/ethereum-optimism/optimism-tutorial)\n- [l1-l2-deposit-withdrawal](https://github.com/ethereum-optimism/l1-l2-deposit-withdrawal)\n\n## Prerequisite Software\n\n- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n- [Node.js](https://nodejs.org/en/download/)\n- [Yarn](https://classic.yarnpkg.com/en/docs/install#mac-stable)\n- [Docker](https://docs.docker.com/engine/install/)\n\n## Build and Run Optimistic Ethereum\n\nEverything we need to build and run Optimistic Rollup is in [Optimistic Ethereum](https://github.com/ethereum-optimism/optimism/tree/develop/ops):\n\n```bash\n$ git clone https://github.com/ethereum-optimism/optimism.git\n$ cd optimism\n$ yarn\n$ yarn build\n```\n\nRun it when the build is done:\n\n```bash\n$ cd ops\n$ docker-compose build\n$ docker-compose up\n```\n\nA few services will be up, including：\n- L1 Ethereum Node (EVM)\n- L2 Optimistic Ethereum Node (OVM)\n- Batch Submitter\n- Data Transport Layer\n- **Deployer**\n- Relayer\n- Verifier\n\n\u003e The environment variable `FRAUD_PROOF_WINDOW_SECONDS` in the deployer service defines how much long the user has to wait when withdrawing. Its default value is `0` second.\n\n\u003e Clean the docker volume when you need to restart the service. Ex: `docker-compose down -v`\n\n### Test Optimistic Ethereum\n\nBefore we continue, we need to make sure that the Optimistic Ethereum operates normally, Especially the relayer. Use integration test to check its functionality:\n\n```bash\n$ cd optimism/integration-tests\n$ yarn build:integration\n$ yarn test:integration\n```\n\nMake sure all the tests related to `L1 \u003c--\u003e L2 Communication` passed before you continue.\n\n\u003e It might take a while (~ 120s) for Optimistic Ethereum to be fully operational. If you fail all the test, try again later or rebuild Optimistic Ethereum from the source again.\n\n## Deploy ERC721 and Gateway Contracts\n\nNext, let's deploy the contract:\n\n```bash\n$ git clone https://github.com/ccniuj/optimistic-rollup-example-erc721.git\n$ cd optimistic-rollup-example-erc721\n$ yarn install\n$ yarn compile\n```\n\nThere are 3 contracts to be deployed:\n- `ExamoleToken` (ERC721), L1\n- `L2DepositedEERC721`, L2\n- `OVM_L1ERC721Gateway`, L1\n\n\u003e `OVM_L1ERC721Gateway` is depoyed on L1 only. As its name suggests, it works as a \"Gateway\" which provides `deposit` and `withdraw` functions. User needs to use a gateway to move funds.\n\n\u003e At the time of writing, ERC721 gateway contract hasn't been implemented by the Optimism team. The contracts used in this example are from this [pull requrest](https://github.com/ethereum-optimism/contracts/pull/325) proposed by [@azf20](https://github.com/azf20).\n\nNext, deploy these contracts by the script:\n\n```bash\n$ node ./deploy.js\nDeploying L1 ERC721...\nL1 ERC721 Contract Address:  0xFD471836031dc5108809D173A067e8486B9047A3\nDeploying L2 ERC721...\nL2 ERC721 Contract Address:  0x09635F643e140090A9A8Dcd712eD6285858ceBef\nDeploying L1 ERC721 Gateway...\nL1 ERC721 Gateway Contract Address:  0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc\nInitializing L2 ERC721...\n```\n\n## ERC721 Mint, Deposit, Transfer and Withdrawal\n\n### Mint ERC721 (L1)\n\nIn the begining, none of the accounts has any ERC721 token:\n\n| L1/L2 | Account | IDs of Owned Token |\n| - | - | - |\n| L1 | Deployer | - |\n| L1 | User | - |\n| L2 | Deployer | - |\n| L2 | User | - |\n\nNext, We will mint 2 tokens to get started. Let's enter hardhat ETH (L1) console:\n\n```bash\n$ npx hardhat console --network eth\nWelcome to Node.js v16.1.0.\nType \".help\" for more information.\n\u003e \n```\n\nInitialize deployer and user:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e let accounts = await ethers.getSigners()\n\u003e let deployer = accounts[0]\n\u003e let user = accounts[1]\n```\n\nInstantiate `ExampleToken` (ERC721) and `OVM_L1ERC721Gateway` contracts. Their contract addresses are in the outputs of the deploy script:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e let ERC721_abi = await artifacts.readArtifact(\"ExampleToken\").then(c =\u003e c.abi)\n\u003e let ERC721 = new ethers.Contract(\"0xFD471836031dc5108809D173A067e8486B9047A3\", ERC721_abi)\n\u003e let Gateway_abi = await artifacts.readArtifact(\"OVM_L1ERC721Gateway\").then(c =\u003e c.abi)\n\u003e let Gateway = new ethers.Contract(\"0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc\", Gateway_abi)\n```\n\nUse deployer to mint 2 tokens:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e await ERC721.connect(deployer).mintToken(deployer.address, \"foo\")\n{\n  hash: \"...\",\n  ...\n}\n\u003e await ERC721.connect(deployer).mintToken(deployer.address, \"bar\")\n{\n  hash: \"...\",\n  ...\n}\n```\n\n\u003e Only the owner of the ExampleToken contract can mint.\n\nConfirm the balance of the owner of tokens:\n\n```javascript\n\u003e await ERC721.connect(deployer).balanceOf(deployer.address)\nBigNumber { _hex: '0x02', _isBigNumber: true } // 2\n\u003e await ERC721.connect(deployer).ownerOf(1)\n'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer\n\u003e await ERC721.connect(deployer).ownerOf(2)\n'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer\n```\n\n### Deposit ERC721 （L1 =\u003e L2）\n\nHere are the current balances:\n\n| L1/L2 | Account | IDs of Owned Token |\n| - | - | - |\n| L1 | Deployer | 1, 2 |\n| L1 | User | - |\n| L2 | Deployer | - |\n| L2 | User | - |\n\nNext, approve `OVM_L1ERC721Gateway` to transfer the token which has `2` as TokenID:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e await ERC721.connect(deployer).approve(\"0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc\", 2)\n{\n  hash: \"...\",\n  ...\n}\n```\n\nCall `deposit` at `OVM_L1ERC721Gateway` contract to deposit this token:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e await Gateway.connect(deployer).deposit(2)\n{\n  hash: \"...\",\n  ...\n}\n```\n\nConfirm if the deposit is successful from Optimistic Ethereum (L2) console:\n\n```bash\n$ npx hardhat console --network optimism\nWelcome to Node.js v16.1.0.\nType \".help\" for more information.\n\u003e \n```\n\nInitialize Deployer and User:\n\n```javascript\n// In Hardhat Optimism Console\n\n\u003e let accounts = await ethers.getSigners()\n\u003e let deployer = accounts[0]\n\u003e let user = accounts[1]\n```\n\nInstantiate `L2DepositedERC721` contract. Its contract address is in the outputs of the deploy script:\n\n```javascript\n// In Hardhat Optimism Console\n\n\u003e let L2ERC721_abi = await artifacts.readArtifact(\"OVM_L2DepositedERC721\").then(c =\u003e c.abi)\n\u003e let L2DepositedERC721 = new ethers.Contract(\"0x09635F643e140090A9A8Dcd712eD6285858ceBef\", L2ERC721_abi)\n```\n\nConfirm if the deposit is successful:\n\n```javascript\n// In Hardhat Optimism Console\n\n\u003e await L2DepositedERC721.connect(deployer).balanceOf(deployer.address)\nBigNumber { _hex: '0x01', _isBigNumber: true } // 1\n\u003e await L2DepositedERC721.connect(deployer).ownerOf(2)\n'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer\n```\n\n### Transfer ERC721 （L2 \u003c=\u003e L2）\n\nHere are the current balances:\n\n| L1/L2 | Account | IDs of Owned Token |\n| - | - | - |\n| L1 | Deployer | 1 |\n| L1 | User | - |\n| L2 | Deployer | 2 |\n| L2 | User | - |\n\nNext, let's transfer some funds from deployer to user:\n\n```javascript\n// In Hardhat Optimism Console\n\n\u003e await L2DepositedERC721.connect(user).balanceOf(user.address)\nBigNumber { _hex: '0x00', _isBigNumber: true } // 0\n\u003e await L2DepositedERC721.connect(deployer).transferFrom(depoyer.address, user.address, 2)\n{\n  hash: \"...\"\n  ...\n}\n\u003e await L2DepositedERC721.connect(user).balanceOf(user.address)\nBigNumber { _hex: '0x01', _isBigNumber: true } // 1\n\u003e await L2DepositedERC721.connect(user).ownerOf(2)\n'0x70997970C51812dc3A010C7d01b50e0d17dc79C8' // user\n```\n\n### Withdraw ERC721 （L2 =\u003e L1）\n\nHere are the current balances:\n\n| L1/L2 | Account | IDs of Owned Token |\n| - | - | - |\n| L1 | Deployer | 1 |\n| L1 | User | - |\n| L2 | Deployer | - |\n| L2 | User | 2 |\n\nNext, let's withdraw the funds via account user. Call `withdraw` at `L2DepositedERC721` contract:\n\n```javascript\n// In Hardhat Optimism Console\n\n\u003e await L2DepositedERC721.connect(user).withdraw(2)\n{\n  hash: \"...\"\n  ...\n}\n\u003e await L2DepositedERC721.connect(user).balanceOf(user.address)\nBigNumber { _hex: '0x00', _isBigNumber: true }\n```\n\nFinally, let's confirm if the withdrawal is successful on L1:\n\n```javascript\n// In Hardhat ETH Console\n\n\u003e await ERC721.connect(user).balanceOf(user.address)\nBigNumber { _hex: '0x01', _isBigNumber: true } // 1\n\u003e await ERC721.connect(deployer).balanceOf(deployer.address)\nBigNumber { _hex: '0x01', _isBigNumber: true } // 1\n\u003e await ERC721.connect(user).ownerOf(2)\n'0x70997970C51812dc3A010C7d01b50e0d17dc79C8' // user\n```\n\n\u003e Since the `FRAUD_PROOF_WINDOW_SECONDS` is set to be `0` second, you don't need to wait too long before the fund is withdrawn back to L1.\n\nAfter all the operations, here are the final balances:\n\n| L1/L2 | Account | IDs of Owned Token |\n| - | - | - |\n| L1 | Deployer | 1 |\n| L1 | User | 2 |\n| L2 | Deployer | - |\n| L2 | User | - |\n\n## Reference\n\n- [OVM Deep Dive](https://medium.com/ethereum-optimism/ovm-deep-dive-a300d1085f52)\n- [(Almost) Everything you need to know about Optimistic Rollup](https://research.paradigm.xyz/rollups)\n- [How does Optimism's Rollup really work?](https://research.paradigm.xyz/optimism)\n- [Optimistic Rollup Official Documentation](https://community.optimism.io/docs/)\n- [Ethers Documentation (v5)](https://docs.ethers.io/v5/)\n- [Optimistic Rollup Example: ERC20(Github)](https://github.com/ccniuj/optimistic-rollup-example-erc20)\n- [Optimism (Github)](https://github.com/ethereum-optimism/optimism)\n- [optimism-tutorial (Github)](https://github.com/ethereum-optimism/optimism-tutorial)\n- [l1-l2-deposit-withdrawal (Github)](https://github.com/ethereum-optimism/l1-l2-deposit-withdrawal)\n- [Proof-of-concept ERC721 Bridge Implementation (Github)](https://github.com/ethereum-optimism/contracts/pull/325)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccniuj%2Foptimistic-rollup-example-erc721","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fccniuj%2Foptimistic-rollup-example-erc721","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccniuj%2Foptimistic-rollup-example-erc721/lists"}