📜 [專欄新文章] Solidity Data Collision
✍️ Wias Liaw
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
這是一篇關於 Proxy Contract 和 delegatecall 的注意事項。
Delegatecall
當 A 合約對 B 合約執行 delegatecall 時,B 合約的函式會被執行,但是對 storage 的操作都會作用在 A 合約上。舉例如下:
但是假如多加了一個 other 欄位在 _value 之前,執行合約之後反而是 other 欄位被更改了。
Storage Layout
了解上面的合約之前要先了解 Solidity 怎麼儲存 State Variables。Solidity Storage 以 Slot 為單位,每個 Slot 可以儲存 32 bytes 的資訊,一個 Contract 擁有 2**256 個 Slot。上述可以寫成一個映射關係 mapping(uint256 => bytes32) slots。
Solidity 會從 Slot Index 為零開始分配給 State Variable。
除了 mapping 和 dynamically-sized array,其他的 State Variable 會從 index 為零的 slot 開始被分配。
沒有宣告確切大小的 Array 會以 Slot Index 計算出一個雜湊值並將其作為 Slot Index。透過計算 keccak256(slot) 可以得知 _arr[0] 被存在哪裡,如果要取得 _arr[1] 則將計算出來的雜湊加上 Array 的 index 即可。
Mapping 則是以 Slot Index 和 Key 計算出一個雜湊值並將其作為 Slot Index。透過計算 keccak256(key, slot) 可以得到 mapping(key => value) 被存在哪。
Storage Collision
回到 DelegateExample_v2 的合約,對 B 來說, add 最後儲存加法的 Slot Index 為零,所以使用 A 的 Storage 執行 B 的函式結果自然會儲存在 A 的 other 裡,其 Slot Index 為 0。
這個問題也發生在 Proxy Contract,Layout 如下,當有需要更改 _owner 的操作,就會順帶把 _implementation 也更改了。
|Proxy |Implementation ||--------------------------|-------------------------||address _implementation |address _owner | <= collision|... |mapping _balances || |uint256 _supply || |... |
OpenZeppelin 處理的手法也很簡單,就是將 _implementation 換地方擺。以特定字串的雜湊值作為 Slot Index,儲存 Implementation 的地址。
|Proxy |Implementation ||--------------------------|-------------------------||... |address _owner ||... |mapping _balances ||... |uint256 _supply ||... |... ||address _implementation | | <= specified|... | |
openzeppelin-contracts/ERC1967Upgrade.sol at 83644fdb6a9f75a652d2fe2d96cb26073a14f6f8 · OpenZeppelin/openzeppelin-contracts
hardhat-storage-layout
如何知道合約的 Storage Layout 呢?這邊推薦一個 Hardhat Plugin,按照文件就能得到合約的 Storage Layout。
Ethereum development environment for professionals by Nomic Labs
Reference
Understanding Ethereum Smart Contract Storage
Collisions of Solidity Storage Layouts
Proxy Upgrade Pattern - OpenZeppelin Docs
Solidity Data Collision was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌
同時也有14部Youtube影片,追蹤數超過123萬的網紅HMS2 - ハムスターのミニチュア工房2,也在其Youtube影片中提到,How to make a miniature Water cooler. みなさんこんにちは! 今回はミニチュアのウォーターサーバーを作ります。 ウォーターサーバー本体はプラバンを加工して作り、水ボトルは色付きの塩ビ板で作ります。 デザインは複数のウォーターサーバーを見て気にいったポイントを真似...
「hardhat」的推薦目錄:
- 關於hardhat 在 Taipei Ethereum Meetup Facebook 的最佳解答
- 關於hardhat 在 Taipei Ethereum Meetup Facebook 的精選貼文
- 關於hardhat 在 Taipei Ethereum Meetup Facebook 的最佳貼文
- 關於hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最讚貼文
- 關於hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最讚貼文
- 關於hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最佳解答
- 關於hardhat 在 NomicFoundation/hardhat - GitHub 的評價
- 關於hardhat 在 Solidity 智能合約開發前|環境建置與安裝|Hardhat by 陳大再 的評價
- 關於hardhat 在 How to stop a hardhat node running on localhost? 的評價
- 關於hardhat 在 HardHat Engineer - Facebook 的評價
- 關於hardhat 在 Time-dependent tests with Hardhat? 的評價
hardhat 在 Taipei Ethereum Meetup Facebook 的精選貼文
📜 [專欄新文章] Tornado Cash 實例解析
✍️ Johnson
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
Tornado Cash 是一個使用 zk-SNARKs 建立的 Dapp,它實現了匿名的代幣交易,這篇文章就用一些程式碼片段,來分享它是怎麼運作的。
本文為 Tornado Cash 研究系列的 Part 3,本系列以 tornado-core 為教材,學習開發 ZKP 的應用,另兩篇為:
Part 1:Merkle Tree in JavaScript
Part 2:ZKP 與智能合約的開發入門
Special thanks to C.C. Liang for review and enlightenment.
我們知道在以太坊上的交易紀錄都是公開的,你可以在 etherscan 上看到某個地址的所有歷史交易紀錄,當然地址是合約的話也是一樣。
也許創建一個新的錢包和地址就好了?假設一個情境是 Alice 想要匿名傳送 1 ETH 給 Bob,Alice 原本的錢包是 A,但她不想讓 A 地址傳給 Bob 的交易紀錄被看到,所以 Alice 創建另一個錢包 B,顯然 B 錢包是空的,Alice 必須把 A 錢包的 1 ETH 傳到 B 錢包,再用 B 錢包的地址傳給 Bob。
但問題就在於,只要追蹤 B 錢包的地址,就能看到 B 的歷史交易紀錄中 A 錢包曾經打幣給 B 錢包,於是到頭來交易還是被追蹤到了。
Tornado Cash 的解決方案,簡單來說,它是一份合約,當你要匿名傳送代幣時,就把一定數量的幣丟進合約裡 (Deposit),此時你會拿到一個 note,長得像這樣:
tornado-eth-0.1-5-0x3863c2e16abc85d72b64d78c68fca5936db2501832e26345226efdfb2bc45804977f167d86b711bb6b4095ddaa646ec93f0a93ac4884a66c1d881f4fc985
note 就是一串字串,擁有這字串的人,就能提領 (Withdraw) 剛剛傳入合約的代幣。握有 note 就代表擁有提款的權利,所以 note 一旦被別人知道,別人就可以把錢給提走。
其中,後面那段亂碼,本篇文章就以「秘密」來稱呼,這個秘密是由 secret 與 nullifier 組成,而這兩個都是在鏈下隨機產生的亂數。
因此 Tornado 的合約基本上會有兩個函式:
Deposit
Withdraw
有興趣的人可以先到 Dapp 上先玩一次看看,使用 Goerli 測試網,這裡可以領 Goerli 的代幣:https://goerli-faucet.slock.it/
Deposit
我們就從 Deposit 開始說起,簡單來說, Deposit 是將資料儲存到合約的 Merkle Tree 上。
剛剛提到的秘密,它是在鏈下產生,由 secret 跟 nullifier 組成,合在一起之後也稱作 preimage,因為我們要對這個 preimage 進行 hash,就會成為 commitment。
合約中 Deposit 如下:
deposit 除了傳送代幣到合約之外,需填入一個參數 _commitment。
我們對 preimage 使用 Pedersen 作為 hash function 加密後產生 commitment,以偽代碼表示如下:
const preimage = secret + nullifier;const commitment = pedersenHash(preimage);
這個 commitment 會成為 Merkle Tree 的葉子,所以合約中的 _insert(commitment) 來自 MerkleTreeWithHistory.sol 的合約,將我們的資料插入 Merkle Tree,然後回傳一個 index 給你,告訴你這個 commitment 在 Merkle Tree 上的位置,最後一起發布成公開的 Deposit 事件。
我們知道 MerkleTree 是將一大筆資料兩兩做雜湊後產生一個唯一值 root,這個 root 就是合約上所儲存的歷史資料。
root 的特性就是只要底下的資料一有更動,就會重新產生新的 root。
所以只要一有用戶 deposit ,就會插入新的葉子到 Merkle Tree 上,於是就會產生新的 root,所以在合約中有一個陣列是用來儲存所有的 root 的 roots:
bytes32[ROOT_HISTORY_SIZE] public roots;
roots 是用來紀錄每個 deposit 的歷史,每一次 deposit 都會創造新的 root,而所有 root 都會被儲存進 roots 裡,於是當你要提領的時候,就要證明你的 commitment 所算出的 root 曾經出現在 roots 裡,代表曾經有 deposit 的動作,因此才可以進行提領。
Withdraw
在 Deposit 之前 Tornado Cash 就會在鏈下產生秘密後交給使用者,擁有這個秘密的人等於擁有提款的權利。
提領的時候,秘密會在鏈下計算後產生 proof,proof 是 withdraw 需要的參數,所以只要確保這個 proof 能夠被驗證,那麼代幣的接收地址 (recipient) 就可以隨便我們填,只要不填上當初拿來 deposit 用的地址,基本上就做到匿名交易的效果了。
也就是說,產生這個 proof 並提交給合約,能夠證明此人知道秘密,但卻不告訴合約秘密本身是什麼。
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant;
我們可以清楚看到 withdraw 函式裡沒有接收有關秘密的任何資訊作為參數,也就是秘密不會與合約有所接觸,也不會暴露在 etherscan 上。
回顧 ZKP 所帶來的效果:
鏈下計算
隱藏秘密
在 Tornado Cash 的例子中,我們用秘密來產生證明,完成的鏈下計算包括:
將秘密 hash 成 commitment
算出 Merkle Tree 的 root。
以下是簡化後的 withdraw.circom:
template Withdraw(levels) { signal input root; signal input nullifierHash;
signal private input nullifier; signal private input secret; signal private input pathElements[levels]; signal private input pathIndices[levels];
component hasher = CommitmentHasher(); // Pedersen hasher.nullifier <== nullifier; hasher.secret <== secret; hasher.nullifierHash === nullifierHash;
component tree = MerkleTreeChecker(levels); // MiMC tree.leaf <== hasher.commitment; tree.root <== root; for (var i = 0; i < levels; i++) { tree.pathElements[i] <== pathElements[i]; tree.pathIndices[i] <== pathIndices[i]; }}
component main = Withdraw(20);
從上述代碼就可以看出這份 circuit 的 private 變數有:
secret
nullifier
pathElements
pathIndices
而 public 變數有:
root
nullifierHash
如同我們一開始說過的,秘密就是指 secret 與 nullifier。這裡進行的鏈下計算就是對 secret 與 nullifier 雜湊成 commitment。而使用的 hash function 叫做 Pedersen。
在進行 Merkle Tree 的計算之前,我們還檢查了 nullifier 雜湊後的 nullifierHash 跟 public 變數 nullifierHash 是不是一樣的。
hasher.nullifierHash === nullifierHash;
接下來,開始計算 Merkle Proof,用意是確認經過雜湊後的 commitment 有沒有出現在 Merkle Tree 上,所以我們的 private input 還有 pathElements 與 pathIndices(詳情參考 Part 1 Merkle Tree in JavaScript),讓它跑一趟 Merkle Proof 的計算,最後就能夠算出一個 root,再確認計算後的 root 與我們的 public 變數 root 是否一樣。
tree.root <== root;
於是我們就能產生一個 ZKP 的證明 — 證明 private 變數:secret, nullifier, pathElements, pathIndices 可以計算出 public 變數:root 與 nullifierHash。
把這個證明提交給合約,合約透過 Verifier 驗證 proof 是否正確,以及必須事先確認:
public 變數 root 有在合約的 roots 裡面。
public 變數 nullifierHash 在合約中是第一次出現。
以下附上完整的 withdraw 原始碼:
必須注意 ZKP 是向合約證明使用者填入的 secret 和 nullifier 可以計算出某個 root,但無法保證這個 root 曾經在合約的 roots 歷史上。
所以合約的 withdraw 中,除了 verifyProof 之外,還要事先檢查 ZKP 算出來的 root 是不是真的在歷史上發生過,所以需要 isKnownRoot 的檢查:
function isKnownRoot(bytes32 _root) public view returns(bool)
必須先檢查 isKnownRoot 後才能進行 verifyProof。
經過 verifyProof 驗證成功後,合約就開始進行提款的動作,也就會將代幣傳到 recipient 的地址,最後拋出 Withdrawal 的事件。
nullifier 與 nullifierHash
為什麼我們的秘密不是只有 secret 還要額外加一個 nullifier?
簡單來說,這是為了防止已經提領過的 note 又再提領一次,也就是所謂的 double spend。
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
可以看到 withdraw 需要填入參數 nullifierHash,跟 isKnownRoot 一樣的狀況,我們需要對電路的 public 變數先經過一層檢查之後,才能帶入到 verifyProof 裡面。
nullifierHash 可以理解為這個 note 的 id,但它不會連結到 deposit,因此可以用來紀錄這個 note 是否已經被提領過。
所以當 verifyProof 驗證成功之後,我們要紀錄 nullifierHash 已完成提領:
nullifierHashes[_nullifierHash] = true;
有關為什麼需要事先檢查 public 變數後,才能帶入 verifyProof ,可以參考 Part 2:ZKP 與智能合約的開發入門 提到的 publicSignals 的部分。
附上 Tornado Cash 的架構圖:
簡化版的 tornado-core
tornado-core 的程式碼很簡潔漂亮,所以我模仿該專案自己實作一遍:
simple-tornado:https://github.com/chnejohnson/simple-tornado
這份專案只完成了 tornado-core 的核心部分,不一樣的是我的開發環境使用 hardhat 與 ethers 寫成,而 circom 與 snarkjs 使用官方當前的版本,合約用 0.7.0,測試使用 Typescript 。
比起兩年前的 tornado-core ,simple-tornado 使用的技術更新,可能更適合初學者理解這份專案,但是它有 bug…我在 issues 的地方有紀錄說明。
在開發的過程中,我的順序是先從最小單位的 MiMC hash function 開始玩,發現必須 javascript 算一次 hash、solidity 算一次、circom 再算一次,確保這三個語言對同一個值算出同樣的 hash 之後,才能放心去做更複雜的 Merkle Tree。
總結
我們可以看到 Tornado Cash 簡單的兩個函式:Deposit 與 Withdraw,透過將代幣送入合約後再提領到另一個地址的流程,應用 ZKP 達成匿名的交易。
除了斷開 Deposit 與 Withdraw 的地址關聯性之外,Tornado Cash 還有做了一層「藏樹於林」的隱私防護,這部份的解釋就請參考 ZKP 讀書會 Tornado Cash。
網路上很多關於 ZKP 的文章或專案都是在 2019 年後出產的,經過許多人對這項技術的嘗試,讓我們對 ZKP 有了更清晰的理解,如今兩年後,開發工具也變得更加成熟,期待未來在 web 隱私議題上能看到更多 ZKP 大放異彩的應用。
原始碼
tornado-core
simple-tornado
參考資料
ZKP 讀書會 Tornado Cash
Tornado Privacy Solution Cryptographic Review
Tornado Cash 實例解析 was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌
hardhat 在 Taipei Ethereum Meetup Facebook 的最佳貼文
📜 [專欄新文章] Optimistic Rollup 就這樣用(2)
✍️ Juin Chiu
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
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,部署於 L1
L2DepositedEERC721,部署於 L2
OVM_L1ERC721Gateway,部署於 L1
OVM_L1ERC721Gateway 只部署在 L1 上,顧名思義它就是 L1 <=> L2 的「門戶」,提供 Deposit / Withdraw 兩個基本功能,使用者必須透過這個合約來進出 L2。
雖然 OVM_L1ERC20Gateway 是 Optimistic Rollup 官方提供的合約。但是開發者也可以依需求自行設計自己的「門戶」。
OVM_L1ERC20Gateway 目前沒有 Optimism 的官方實作,本演示所使用的 ERC721 Gateway 合約來自這個提案,目前尚未成為官方標準。
接下來,我們直接用腳本進行部署:
$ node ./deploy.jsDeploying L1 ERC721...L1 ERC2721 Contract Address: 0xFD471836031dc5108809D173A067e8486B9047A3Deploying L2 ERC721...L2 ERC721 Contract Address: 0x09635F643e140090A9A8Dcd712eD6285858ceBefDeploying L1 ERC721 Gateway...L1 ERC721 Gateway Contract Address: 0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547ccInitializing L2 ERC721...
ERC721 鑄造、儲值、轉移與提領
鑄造(L1)
初始狀態如下,所有帳戶皆尚未持有任何代幣:
接下來,我們將鑄造 2 個代幣以進行接下來的演示。首先,進入 ETH(L1) 的 Console:
$ npx hardhat console --network ethWelcome 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 optimismWelcome 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)
Optimistic Rollup 就這樣用(2) was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌
hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最讚貼文
How to make a miniature Water cooler.
みなさんこんにちは!
今回はミニチュアのウォーターサーバーを作ります。
ウォーターサーバー本体はプラバンを加工して作り、水ボトルは色付きの塩ビ板で作ります。
デザインは複数のウォーターサーバーを見て気にいったポイントを真似て作りました。
ところでボトル作りに使った色付きの塩ビ板は今回初めて使ったのですがかなりいいです。色を塗る必要もなく簡単にウォーターサーバーでよく見るクリアーブルーの水ボトルを再現できました。
これ、塩ビ板の色を変えることで他のミニチュア作りも簡単になるのでは?っと思案中です。
【ミニチュア製作の簡単な流れは以下のようになります】
1.水ボトルを作る。00:06
2.ウォーターサーバー本体を作る。03:47
3.出水口を作る。05:37
4.トレーを作る。06:45
【その他のミニチュア動画】
◆ミニチュア消毒液作ってみた!! DIY Miniature Hand Sanitizer
https://youtu.be/FQu32YRjGBk
◆ミニチュアホームベーカリー作ってみた! DIY Miniature Bread machine
https://youtu.be/xKTdRxwf-2c
◆工事現場で見かけるバリケードのミニチュア作ってみた! DIY Miniature barricade
https://youtu.be/bow0fZOTc7s
◆プラバン加工してミニチュアシャベル・スコップを作ってみた!! DIY Miniature Shovel
https://youtu.be/6TXypl6h4vs
◆工事用ヘルメットのミニチュア作ってみた!! DIY Miniature Hardhat
https://youtu.be/Pfm4As6JwLY
◆ミニチュアカーブミラー作ってみた! DIY Miniature Traffic mirror
https://youtu.be/G2nUvkGakso
◆ミニチュア電話ボックス作ってみた! DIY Miniature Phone booth
https://youtu.be/ARI6wJlz1O4
◆新聞紙でタウンページ風のミニチュア本作ってみた! DIY Miniature Phone book
https://youtu.be/EoUa2BgoVN4
◆ DIY Miniature Portable Stove ミニチュアカセットコンロ作り
https://youtu.be/I-TqCaGaT5A
◆ DIY Miniature Air condition ミニチュアエアコン作り
https://youtu.be/0qiT7C1BSjE
【Please Subscribe チャンネル登録はこちらです】
http://www.youtube.com/channel/UCseOe3MfK8d2IjK2NoEpnaA?sub_confirmation=1
#miniature
#ミニチュア
#ウォーターサーバー
#Water cooler
#ハンドメイド
#作ってみた
#スクラッチビルド
#scratch building
![post-title](https://i.ytimg.com/vi/HfFa5tQeGXw/hqdefault.jpg)
hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最讚貼文
How to make a miniature bread machine.
みなさんこんにちは!
今回は中華鍋、おたま、鍋の台のミニチュアをプラバンなどのプラ材を加工して作ります。
今回は形が割と単純なのでプラバンの加工にいつものバキュームフォーマーは使わずおこなっていきます。
【ミニチュア製作の簡単な流れは以下のようになります】
1.中華鍋を作る。
2.おたまを作る。
3.中華鍋の台を作る。
【その他のミニチュア動画】
◆ミニチュアホームベーカリー作ってみた! DIY Miniature Bread machine
https://youtu.be/xKTdRxwf-2c
◆工事現場で見かけるバリケードのミニチュア作ってみた! DIY Miniature barricade
https://youtu.be/bow0fZOTc7s
◆プラバン加工してミニチュアシャベル・スコップを作ってみた!! DIY Miniature Shovel
https://youtu.be/6TXypl6h4vs
◆工事用ヘルメットのミニチュア作ってみた!! DIY Miniature Hardhat
https://youtu.be/Pfm4As6JwLY
◆ミニチュアカーブミラー作ってみた! DIY Miniature Traffic mirror
https://youtu.be/G2nUvkGakso
◆ミニチュア電話ボックス作ってみた! DIY Miniature Phone booth
https://youtu.be/ARI6wJlz1O4
◆新聞紙でタウンページ風のミニチュア本作ってみた! DIY Miniature Phone book
https://youtu.be/EoUa2BgoVN4
◆ DIY Miniature Portable Stove ミニチュアカセットコンロ作り
https://youtu.be/I-TqCaGaT5A
◆ DIY Miniature Air condition ミニチュアエアコン作り
https://youtu.be/0qiT7C1BSjE
【Please Subscribe チャンネル登録はこちらです】
http://www.youtube.com/channel/UCseOe3MfK8d2IjK2NoEpnaA?sub_confirmation=1
#miniature
#ミニチュア
#Wok
#中華鍋
#ハンドメイド
#作ってみた
#スクラッチビルド
#scratch building
![post-title](https://i.ytimg.com/vi/Olq9B_Jx3OY/hqdefault.jpg)
hardhat 在 HMS2 - ハムスターのミニチュア工房2 Youtube 的最佳解答
How to make a miniature bread machine.
みなさんこんにちは!
今回は今までの人生で一番お世話になったと言っても過言ではない消毒液のミニチュアを作ります。
消毒液のボトル部分は塩ビ板をバキュームフォームして作り、中身の消毒液はUVレジンで表現、シャワーポンプはプラパイプと丸棒のプラ材を組み合わせて作っていきます。
【ミニチュア製作の簡単な流れは以下のようになります】
1.プラバンを利用したボトル部分の原型作り。
2.バキュームフォーム作業で塩ビ板をボトルの形に成形。
3.プラパイプと丸棒のプラ材を加工したシャワーポンプ作り。
4.ボトルにUVレジンを入れ作業。
5.ラベルシールを貼って完成。
病院や飲食店はもちろんですが、住居のドールハウスにこのミニチュアs消毒液を設置すれば「衛生面に気を使っています」みたいな雰囲気を出せそうです。
【その他のミニチュア動画】
◆ミニチュアホームベーカリー作ってみた! DIY Miniature Bread machine
https://youtu.be/xKTdRxwf-2c
◆工事現場で見かけるバリケードのミニチュア作ってみた! DIY Miniature barricade
https://youtu.be/bow0fZOTc7s
◆プラバン加工してミニチュアシャベル・スコップを作ってみた!! DIY Miniature Shovel
https://youtu.be/6TXypl6h4vs
◆工事用ヘルメットのミニチュア作ってみた!! DIY Miniature Hardhat
https://youtu.be/Pfm4As6JwLY
◆ミニチュアカーブミラー作ってみた! DIY Miniature Traffic mirror
https://youtu.be/G2nUvkGakso
◆ミニチュア電話ボックス作ってみた! DIY Miniature Phone booth
https://youtu.be/ARI6wJlz1O4
◆新聞紙でタウンページ風のミニチュア本作ってみた! DIY Miniature Phone book
https://youtu.be/EoUa2BgoVN4
◆ DIY Miniature Portable Stove ミニチュアカセットコンロ作り
https://youtu.be/I-TqCaGaT5A
◆ DIY Miniature Air condition ミニチュアエアコン作り
https://youtu.be/0qiT7C1BSjE
【Please Subscribe チャンネル登録はこちらです】
http://www.youtube.com/channel/UCseOe3MfK8d2IjK2NoEpnaA?sub_confirmation=1
#miniature
#ミニチュア
#Hand Sanitizer
#消毒液
#ハンドメイド
#作ってみた
#スクラッチビルド
#scratch building
![post-title](https://i.ytimg.com/vi/FQu32YRjGBk/hqdefault.jpg)
hardhat 在 Solidity 智能合約開發前|環境建置與安裝|Hardhat by 陳大再 的推薦與評價
![影片讀取中](/images/youtube.png)
基本開發環境安裝: VS Code & Node.jsSolidity 開發框架環境安裝: Hardhat 章節分享:0:00 Intro0:26 VS Code & Node.js2:34 ... ... <看更多>
hardhat 在 NomicFoundation/hardhat - GitHub 的推薦與評價
Hardhat is an Ethereum development environment for professionals. It facilitates performing frequent tasks, such as running tests, automatically checking ... ... <看更多>