今天將會透過 Lazy Minting 文章理解何謂 Lazy Minting 與實作 Lazy Minting
在主鏈鑄造一個 NFT 通常會花費很多,因為會需要手續費用來處理計算與儲存資料
然而透過一些比較新的技術可以把鑄造 NFT 這個處理程序延後到確認有要賣出 NFT 才去做。這樣做可以讓買 NFT 的人去負擔鑄造的費用,所以鑄造 NFT 的人不需要去鑄造費。
當被購買時才去鑄造 NFT 被稱作 Lazy Minting,這項技術降低了想要鑄造 NFT 的門檻。
在這邊將會使用 nft-school-example/lazy-minit 這個專案來做示範
git clone https://github.com/ipfs-shipyard/nft-school-examples
cd nft-school-examples/lazy-minting
code . # or whichever editor you prefer
lazy minting 不是直接透過呼叫 Contract 的 function 來產生 NFT, NFT 鑄造者會準備一個透過鑄造者密鑰對一些資料所產生出來的簽章。
這些簽章被稱作代金券可以用來贖回 NFT。這些簽章會包含所有關於該 NFT 的資料比如說價錢。這些簽章可以用來證明這個 NFT 鑄造者授權 NFT 的一些行為還有內容。
當購買者想要買一個 NFT,會呼叫 redeem function 來贖回這個簽章。當這個簽章被驗證是正確的並且購買者有被授權可以鑄造 NFT, NFT 就會根據這個簽章內容去製造出來並且轉移給購買者。
以下是贖回資料的範例
struct NFTVoucher {
uint256 tokenId;
uint256 minPrice;
string uri;
bytes signature;
}
贖回資料包含兩個位會紀錄再鏈上的資料:
minPrice 不會被紀錄在鏈上但是透過 redeem function 可以讓鑄造者在建立 NFT 時設定買賣價錢。假設 minPrice > 0 , 購買者需要在呼叫時傳送至少大於 minPrice 的錢來購買。
為了確保贖回資料不會被撰改,這邊需要使用簽章的技術來驗證資料正確性
為了比較良好的使用者體驗,所以 Ethereum 發展出了一個針對結構化資料簽章規格 EIP-712
以下範例用了一個 Javascript 的類別 LazyMinter 就是使用上述 EIP-712 所實作的。因為簽章必須要屬於某個一發佈的 contract 實體,所以必須要提供一個已經發佈的 Contract address 與 ethers.js 的 Signer 用來做 NFT 鑄造的 private key 如下
const lazyminter = new LazyMinter({ myDeployedContract.address, signerForMinterAccount })
而建構贖回資料的邏輯 createVoucher 方法如下
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 設定對應要簽章的 types
接著使用 _signTypedData 來計算簽章
然後把原本的資料跟簽章都包在 javascript 物件回傳回去
為了 lazy minting , 必須要產生一個 Smart Contract 讓 NFT 購買者可以用來鑄造自己想要的 NFT 並且把 NFT 轉移到自己的 account 內,如下面 redeem:
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 並且在鑄造完成後轉移到購買者帳戶