iT邦幫忙

2022 iThome 鐵人賽

DAY 27
1
Web 3

Road Map To DApp Developer系列 第 27

【DAY27】Verification System (Smart Contract I)

  • 分享至 

  • xImage
  •  

Preface

我們將 Client 的 QRCode 在前端做了驗證後,我想要設計一個方式讓這個票卷不能重複使用,原本的想法是利用 Dynamic NFT 的方法,將其 metadata 指向別的地方以表示這張票卷已使用過了。但是由於 ERC-1155 的特性,每一種 id 對應到的是其中一種票卷而不是每個人擁有的不同 token,因此在這邊我想到的做法是將驗證後的票卷燒毀(burn),並再重新 mint 一張新的 token 到使用者的地址。

After Verify OffChain

Burn

在之前 ERC-721 介紹的時候有提到 Transfer() 這個 event,當 mint() 的時候,其中的 _fromzero address;反過來當 burn() 時,其中的 _to 會是 zero address

OZ - burn

因此可以來觀察 Openzeppelin 的合約中是怎麼實作 _burn() 這個 funciton 的:

/**
 * @dev Destroys `amount` tokens of token type `id` from `from`
 *
 * Emits a {TransferSingle} event.
 *
 * Requirements:
 *
 * - `from` cannot be the zero address.
 * - `from` must have at least `amount` tokens of token type `id`.
 */
function _burn(
    address from,
    uint256 id,
    uint256 amount
) internal virtual {
    require(from != address(0), "ERC1155: burn from the zero address");

    address operator = _msgSender();

    uint256[] memory ids = _asSingletonArray(id);
    uint256[] memory amounts = _asSingletonArray(amount);

    _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

    uint256 fromBalance = _balances[id][from];
    require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
    unchecked {
        _balances[id][from] = fromBalance - amount;
    }

    emit TransferSingle(operator, from, address(0), id, amount);

    _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
}

首先傳入的參數是

  • address: from : 燒毀 token 的地址。
  • uint256: id : 燒毀何種 id 的 token。
  • uint256: amount: 燒毀多少 token。

_asSingletonArray()

_asSingltonArray 可以複製一個動態的 memory array(size=1)裡面只會放一個 element=param 這樣做的原因是因為 OZ 中 ERC1155 的 _beforeTransferToken() 參數需要傳入 array,就與 mintBatch() 的方式類似,可參考這個合約

_beforeTokenTransfer()

_berforeTokenTransfer() 可以在 OZ 的 ERC1155/extensions/ERC1155Supply 中找到。

它做的事情就是去完成 mint()burn() 時對於全部 token 改動的事情,這邊我們專注在 burn()

function _beforeTokenTransfer(        
    address operator,
    address from,
    address to,
    uint256[] memory ids,
    uint256[] memory amounts,
    bytes memory data
) internal virtual override {
    super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

    if (from == address(0)) {
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] += amounts[i];
        }
    }

    if (to == address(0)) {
        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];
            uint256 supply = _totalSupply[id];
            require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
            unchecked {
                _totalSupply[id] = supply - amount;
            }
        }
    }
}

to 為 zero address 時,會遍歷整個 ids,並使用 uncheck{ } 來將總數量減掉 amount。

solidity 0.8 版本後會自行確認溢位(overflow)與下溢(underflow),使用 uncheck 可以讓程式 compile 時不幫你確認,但可以節省 gas fee。

address balance

隨後再使用與 _berforeTokenTransfer() 相同的手法,將 address 中擁有的 token 數量減掉amount

afterTokenTransfer()

雖然在 OZ 的 ERC1155 中有宣告這個函式,但是沒有在其他合約中看到函式定義的部分,我推測這個函式可能是為了讓合約撰寫者想要在 token 被轉移之後作出某些事情而宣告,應該是可以讓使用者自行 override 並實作的。在這邊因為我們想要的效果就是燒毀就好了,因此不需要其他的實作。

Implement In our Contract

這邊我直接 import OZ 的合約:ERC1155Burnable.sol

import "@openzeppelin/contracts@4.7.3/token/ERC1155/extensions/ERC1155Burnable.sol";

contract {
    function ...
        burn(address, id, amount);
}

Verify

最後因為我們是在前端驗證的,因此當驗證成功的時候驗證者會透過前端呼叫這個函式。其中編號 4 的 token 是觀眾紀念票!

...
uint256 constant private AUDIENCE_SOUVENIR = 4;
...
function verifySuccessed(address user) public onlyOwner {
    burn(user, 3, 1);
    _mint(user, 4, 1, "");
}

Closing

我原本的驗證設計是:「驗證者」需要透過 metamask 登入才能花費 gas 執行 verifySuccessed() 的動作,但是當有幾千人或是幾萬人同時進行這樣的動作時,肯定是太慢了。因此明天會將重點放在如何改善這個流程,讓驗證的速度更快更流暢,但同時也需要維護一些安全性上的考量。

Ref:


若有文章內有任何錯誤的地方歡迎指點與討論!非常感謝!

歡迎贊助窮困潦倒大學生
0xd8538ea74825080c0c80B9B175f57e91Ff885Cb4


上一篇
【DAY26】Verification System (Verify On Front-end)
下一篇
【DAY28】Verification System (Smart Contract II - Use Infura to send tx)
系列文
Road Map To DApp Developer30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言