--- tags: it 鐵人賽 30 天, Web 3, ethereum --- # 從以太坊白皮書理解 web 3 概念 - Day25 ## NFT School Day 3 - Lazy Minting 今天將會透過 [Lazy Minting](https://nftschool.dev/tutorial/lazy-minting/) 文章理解何謂 Lazy Minting 與實作 Lazy Minting 在主鏈鑄造一個 NFT 通常會花費很多,因為會需要手續費用來處理計算與儲存資料 然而透過一些比較新的技術可以把鑄造 NFT 這個處理程序延後到確認有要賣出 NFT 才去做。這樣做可以讓買 NFT 的人去負擔鑄造的費用,所以鑄造 NFT 的人不需要去鑄造費。 當被購買時才去鑄造 NFT 被稱作 Lazy Minting,這項技術降低了想要鑄造 NFT 的門檻。 在這邊將會使用 [nft-school-example/lazy-minit](https://github.com/ipfs-shipyard/nft-school-examples) 這個專案來做示範 ## clone 專案到 local ```shell= git clone https://github.com/ipfs-shipyard/nft-school-examples cd nft-school-examples/lazy-minting code . # or whichever editor you prefer ``` ## lazy minting 機制 lazy minting 不是直接透過呼叫 Contract 的 function 來產生 NFT, NFT 鑄造者會準備一個透過鑄造者密鑰對一些資料所產生出來的簽章。 這些簽章被稱作代金券可以用來贖回 NFT。這些簽章會包含所有關於該 NFT 的資料比如說價錢。這些簽章可以用來證明這個 NFT 鑄造者授權 NFT 的一些行為還有內容。 當購買者想要買一個 NFT,會呼叫 redeem function 來贖回這個簽章。當這個簽章被驗證是正確的並且購買者有被授權可以鑄造 NFT, NFT 就會根據這個簽章內容去製造出來並且轉移給購買者。 以下是贖回資料的範例 ```solidity= struct NFTVoucher { uint256 tokenId; uint256 minPrice; string uri; bytes signature; } ``` 贖回資料包含兩個位會紀錄再鏈上的資料: 1. 唯一 token 識別代碼 tokenId 2. token 資料 uri minPrice 不會被紀錄在鏈上但是透過 redeem function 可以讓鑄造者在建立 NFT 時設定買賣價錢。假設 minPrice > 0 , 購買者需要在呼叫時傳送至少大於 minPrice 的錢來購買。 ## 建立一個簽章過的贖回資料 為了確保贖回資料不會被撰改,這邊需要使用簽章的技術來驗證資料正確性 為了比較良好的使用者體驗,所以 Ethereum 發展出了一個針對結構化資料簽章規格 [EIP-712](https://eips.ethereum.org/EIPS/eip-712) 以下範例用了一個 Javascript 的類別 LazyMinter 就是使用上述 [EIP-712](https://eips.ethereum.org/EIPS/eip-712) 所實作的。因為簽章必須要屬於某個一發佈的 contract 實體,所以必須要提供一個已經發佈的 Contract address 與 ethers.js 的 Signer 用來做 NFT 鑄造的 private key 如下 ```javascript= const lazyminter = new LazyMinter({ myDeployedContract.address, signerForMinterAccount }) ``` 而建構贖回資料的邏輯 createVoucher 方法如下 ```javascript= async createVoucher(tokenId, uri, minPrice = 0) { const voucher = { tokenId, uri, minPrice } const domain = await this._signingDomain() const types = { NFTVoucher: [ {name: "tokenId", type: "uint256"}, {name: "minPrice", type: "uint256"}, {name: "uri", type: "string"}, ] } const signature = await this.signer._signTypedData(domain, types, voucher) return { ...voucher, signature, } } ``` 首先需要準備好要簽章的資料放在 const voucher 物件 然後根據 [EIP-712](https://eips.ethereum.org/EIPS/eip-712) 設定對應要簽章的 types 接著使用 _signTypedData 來計算簽章 然後把原本的資料跟簽章都包在 javascript 物件回傳回去 ## 贖回 NFT 流程 為了 lazy minting , 必須要產生一個 Smart Contract 讓 NFT 購買者可以用來鑄造自己想要的 NFT 並且把 NFT 轉移到自己的 account 內,如下面 redeem: ```solidity= function redeem(address redeemer, NFTVoucher calldata voucher) public payable returns (uint256) { // make sure signature is valid and get the address of the signer address signer = _verify(voucher); // make sure that the signer is authorized to mint NFTs require(hasRole(MINTER_ROLE, signer), "Signature invalid or unauthorized"); // make sure that the redeemer is paying enough to cover the buyer's cost require(msg.value >= voucher.minPrice, "Insufficient funds to redeem"); // first assign the token to the signer, to establish provenance on-chain _mint(signer, voucher.tokenId); _setTokenURI(voucher.tokenId, voucher.uri); // transfer the token to the redeemer _transfer(signer, redeemer, voucher.tokenId); // record payment to signer's withdrawal balance pendingWithdrawals[signer] += msg.value; return voucher.tokenId; } ``` 首先會使用 _verify 驗證要產生的NFT 簽章資料是否正確 接者會使用 OpenZeppelin 的 hasRole 來確認該簽章者是否俱備鑄造的能力 接著會確認 NFT 購買者是否傳輸大於 minPrice 的 ETH 如果都符合則開始根據簽章資料來產生 NFT 並且在鑄造完成後轉移到購買者帳戶