---
# System prepended metadata

title: 從以太坊白皮書理解 web 3 概念 - Day25
tags: [' Web 3', it 鐵人賽 30 天, ' ethereum']

---

---
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 並且在鑄造完成後轉移到購買者帳戶



