Optimistic Rollup 就這樣用(2)
ERC721 的儲值、轉移與提領
TL;DR
- 本文會跳過 Optimistic Rollup 的介紹而直接實際演示,關於 Optimistic Rollup 的概念與設計原理筆者將在日後另撰文說明,有興趣的讀者可以先參考下列三篇文章(由淺入深):1. OVM Deep Dive 2. (Almost) Everything you need to know about Optimistic Rollup 3. How does Optimism’s Rollup really work?
- 本文將演示一個 Optimism Rollup 的 ERC721 範例,程式碼在這裡。
- 本演示大量參考了以下範例:Optimistic Rollup Example: ERC20。
- 本演示所使用的 ERC721 Gateway 合約來自這個提案,目前尚未成為官方標準。
環境設置
- Git
- Node.js
- Yarn
- Docker
- Docker-compose
筆者沒有碰到環境相容問題,但是建議都升到最新版本, Node.js 使用
v16.1.0
或以上版本
Optimism 服務啟動
有關 Optimisim 的所有服務,都包裝在 Optimism 這個超大專案當中了,直接使用原始碼進行組建:
$ git clone git@github.com:ethereum-optimism/optimism.git
$ cd optimism
$ yarn
$ yarn build
組建完成後,就可以在本機啟動服務了:
$ cd ops
$ docker-compose build
$ docker-compose up
這個指令會啟動數個服務,包括:
- L1 Ethereum Node (EVM)
- L2 Ethereum Node (OVM)
- Batch Submitter
- Data Transport Layer
- Deployer
- Relayer
- Verifier
Deployer 服務中的一個參數要特別注意:
FRAUD_PROOF_WINDOW_SECONDS
,這個就是 OPtimistic Rollup 的挑戰期,代表使用者出金(Withdraw)需等候的時長。在本篇演示中預設為0
秒。如果有需要重啟,記得把整個 Docker Volume 也清乾淨,例如:
docker-compose down -v
Optimism 整合測試
在繼續接下來的演示之前,我們需要先確認 Optimism 是否有順利啟動,特別是 Relayer 是否運作正常,因此我們需要先進行整合測試:
$ cd optimism/integration-tests
$ yarn build:integration
$ yarn test:integration
確保 L1 <--> L2 Communication
相關測試通過後再繼續執行接下來的演示內容。
啟動服務及部署合約需要花費一些時間,運行一段時間(約 120 秒)之後再執行測試,如果測試結果全部皆為 Fail,可能是 Optimism 尚未啟動完成,再等待一段時間即可。
ERC721 合約部署
Optimism 啟動成功並且完成整合測試後,接下來進行 ERC721 合約的部署。筆者已將合約及部署腳本放在 optimistic-rollup-example-erc721 這個專案中:
$ git clone git@github.com:ethereum-optimism/optimistic-rollup-example-erc721.git
$ cd optimistic-rollup-example-erc721
$ yarn install
$ yarn compile
接下來我們需要部署以下合約:
ERC721
,部署於 L1L2DepositedEERC721
,部署於 L2OVM_L1ERC721Gateway
,部署於 L1
OVM_L1ERC721Gateway
只部署在 L1 上,顧名思義它就是 L1 <=> L2 的「門戶」,提供Deposit
/Withdraw
兩個基本功能,使用者必須透過這個合約來進出 L2。雖然
OVM_L1ERC20Gateway
是 Optimistic Rollup 官方提供的合約。但是開發者也可以依需求自行設計自己的「門戶」。
OVM_L1ERC20Gateway
目前沒有 Optimism 的官方實作,本演示所使用的 ERC721 Gateway 合約來自這個提案,目前尚未成為官方標準。
接下來,我們直接用腳本進行部署:
$ node ./deploy.js
Deploying L1 ERC721...
L1 ERC2721 Contract Address: 0xFD471836031dc5108809D173A067e8486B9047A3
Deploying L2 ERC721...
L2 ERC721 Contract Address: 0x09635F643e140090A9A8Dcd712eD6285858ceBef
Deploying L1 ERC721 Gateway...
L1 ERC721 Gateway Contract Address: 0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc
Initializing L2 ERC721...
ERC721 鑄造、儲值、轉移與提領
鑄造(L1)
初始狀態如下,所有帳戶皆尚未持有任何代幣:
接下來,我們將鑄造 2 個代幣以進行接下來的演示。首先,進入 ETH(L1) 的 Console:
$ npx hardhat console --network eth
Welcome to Node.js v16.1.0.
Type ".help" for more information.
>
取得 Deployer / User 帳戶:
// In Hardhat ETH Console> let accounts = await ethers.getSigners()> let deployer = accounts[0]> let user = accounts[1]
取得 ERC721
及 OVM_L1ERC721Gateway
合約物件,合約地址可以從部署訊息中取得:
// In Hardhat ETH Console> let ERC721_abi = await artifacts.readArtifact("ExampleToken").then(c => c.abi)> let ERC721 = new ethers.Contract("0xFD471836031dc5108809D173A067e8486B9047A3", ERC721_abi)> let Gateway_abi = await artifacts.readArtifact("OVM_L1ERC721Gateway").then(c => c.abi)> let Gateway = new ethers.Contract("0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc", Gateway_abi)
鑄造兩個 ERC721 代幣:
// In Hardhat ETH Console> await ERC721.connect(deployer).mintToken(deployer.address, "foo"){
hash: "...",
...
}> await ERC721.connect(deployer).mintToken(deployer.address, "bar"){
hash: "...",
...
}
只有合約的 Owner(
deployer
) 可以進行鑄造的操作。
確認 Deployer 餘額:
> await ERC721.connect(deployer).balanceOf(deployer.address)BigNumber { _hex: '0x02', _isBigNumber: true } // 2
確認代幣的 TokenID 與 Owner:
> await ERC721.connect(deployer).ownerOf(1)'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer> await ERC721.connect(deployer).ownerOf(2)'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer
儲值(L1 => L2)
完成以上步驟後,目前的狀態如下:
接下來,授權 OVM_L1ERC721Gateway
使用 TokenID 為 2
的代幣:
// In Hardhat ETH Console> await ERC721.connect(deployer).approve("0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc", 2){
hash: "...",
...
}
在 OVM_L1ERC721Gateway
合約呼叫 Deposit
,儲值 TokenID 為 2
的代幣:
// In Hardhat ETH Console> await Gateway.connect(deployer).deposit(2){
hash: "...",
...
}
我們可以到 Optimism (L2) 的 Console 確認入金是否成功:
$ npx hardhat console --network optimism
Welcome to Node.js v16.1.0.
Type ".help" for more information.
>
取得 Deployer / User 帳戶:
// In Hardhat Optimism Console> let accounts = await ethers.getSigners()> let deployer = accounts[0]> let user = accounts[1]
取得 L2DepositedERC721
合約物件,合約地址可以從部署訊息中取得:
// In Hardhat Optimism Console> let L2ERC721_abi = await artifacts.readArtifact("OVM_L2DepositedERC721").then(c => c.abi)> let L2DepositedERC721 = new ethers.Contract("0x09635F643e140090A9A8Dcd712eD6285858ceBef", L2ERC721_abi)
確認入金是否成功:
// In Hardhat Optimism Console> await L2DepositedERC721.connect(deployer).balanceOf(deployer.address)BigNumber { _hex: '0x01', _isBigNumber: true } // 1> await L2DepositedERC721.connect(deployer).ownerOf(2)'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' // deployer
ERC721 轉移(L2 <=> L2)
完成以上步驟後,目前的狀態如下:
接下來,我們在 L2 從 Deployer 轉移代幣給 User:
// In Hardhat Optimism Console> await L2DepositedERC721.connect(user).balanceOf(user.address)BigNumber { _hex: '0x00', _isBigNumber: true } // 0> await L2DepositedERC721.connect(deployer).transferFrom(depoyer.address, user.address, 2){
hash: "..."
...
}> await L2DepositedERC721.connect(user).balanceOf(user.address)BigNumber { _hex: '0x01', _isBigNumber: true } // 1> await L2DepositedERC721.connect(user).ownerOf(2)'0x70997970C51812dc3A010C7d01b50e0d17dc79C8' // user
ERC721 提領(L2 => L1)
完成以上步驟後,目前的狀態如下:
接下來,我們用 User 帳戶提領資金,在 L2DepositedERC721
合約呼叫 Withdraw
:
// In Hardhat Optimism Console> await L2DepositedERC721.connect(user).withdraw(2){
hash: "..."
...
}> await L2DepositedERC721.connect(user).balanceOf(user.address)BigNumber { _hex: '0x00', _isBigNumber: true }
最後,檢查在 L1 是否提領成功:
// In Hardhat ETH Console> await ERC721.connect(user).balanceOf(user.address)BigNumber { _hex: '0x01', _isBigNumber: true } // 1> await ERC721.connect(deployer).balanceOf(deployer.address)BigNumber { _hex: '0x01', _isBigNumber: true } // 1> await ERC721.connect(user).ownerOf(2)'0x70997970C51812dc3A010C7d01b50e0d17dc79C8' // user
由於挑戰期為 0 秒,因此提領幾乎無需等待時間,頂多只需數秒鐘
做完上述所有操作,最終狀態應該如下:
總結
本文演示了:
- Optimistic Rollup 相關服務的本機部署
- ERC721 L1 => L2 的儲值(Deposit)
- ERC721 L2 帳戶之間轉移(Transfer)
- ERC721 L2 => L1 的提領(Withdraw)
筆者未來將繼續擴充此系列的教學內容,例如支援其他標準的合約如 ERC1155,以及如何運行 Optimistic Rollup 生態系中最重要的驗證者(Verifier),敬請期待。
參考資料
- OVM Deep Dive
- (Almost) Everything you need to know about Optimistic Rollup
- How does Optimism’s Rollup really work?
- Optimistic Rollup Official Documentation
- Ethers Documentation (v5)
- Optimistic Rollup Example: ERC20(Github)
- Optimism (Github)
- optimism-tutorial (Github)
- l1-l2-deposit-withdrawal (Github)
- Proof-of-concept ERC721 Bridge Implementation (Github)