😺

限られた人だけがMintできるsmart contractを作成する

2022/12/08に公開

概要

最近では誰もがmintできるようにするpublic sale前に、限られた人(allow list)だけmintができるsmart contractをよくみかけます。
Merkle Treesを使って限られた人だけがmintできるSmart Contractを開発したのでその知見をまとめます

最初に

smart contractの基本的な書き方などはスキップします。
書いたことがない人は以下の記事を読んでください。めちゃくちゃわかりやすいです
https://zenn.dev/razokulover/articles/7db2340f14c2cd

参考

こちらの実装を参考にさせてもらいました!
contractの実装、web側での呼び出し方のサンプルが書いてあるのでとても参考になります。
https://github.com/straightupjac/nft-merkle-allowlist-scaffold

ライブラリ

openzepplinのライブラリを使用します
https://docs.openzeppelin.com/contracts/3.x/api/cryptography
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol

やってみる

# hardhatと関連ライブラリのインストール
% npm install --save-dev hardhat
% npm install @openzeppelin/contracts web3

# コントラクトの作成
% npx hardhat

サンプルなので、Create a basic sample projectを選択。他の選択肢もyesで進みます。
テンプレートが作られます。

簡単なmintをするコントラクトを作ってみます。

デプロイしてmintしてみましょう。
今の段階では、誰もができる状態です。

ではここにMerkle Treesを使ってallowlistとして登録したユーザーのみがmintできるようにします

Merkle Treesの作成

Merkle Treeの作成にはmerkleproofjsを使います
ハッシュ関数は、Solidityのデフォルトのハッシュ関数であるKeccak256を利用するためこのライブラリもインストールします。

npm i merkleproofjs 
npm i keccak256

まずはallowlistに登録するEthereum アドレスのリストを作成です。
今回は例としてランダムなアドレスを配列に追加します。

const { MerkleTree } = require("merkletreejs");
const keccak256 = require("keccak256");

// List of 7 public Ethereum addresses
let addresses = [
  "0x2088aC4Cb16B05E11A3F923819D6805a573FCb03",
  "0x...",
  "0x...",
];

const leaves = addresses.map((addr) => keccak256(addr));
const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true });
const allowlistRootHash = merkleTree.getHexRoot();

console.log("allowlistRootHash:", allowlistRootHash);

Smart Contract

OpenZepplinのmerkletreeライブラリを使います。
https://docs.openzeppelin.com/contracts/3.x/api/cryptography

Smart Contract上でMarkleTreeの変更可能にする場合は、他の人に設定できないようにOwnerbleにしましょう。

テスト用のSmart Contractを書いてみました。
動作は保証していません。

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";


contract Sample is ERC1155, Ownable {

    bytes32 merkleRoot;
    using MerkleProof for bytes32[];
    string public name = "Sample";
    string public symbol = "Sample";

    constructor() ERC1155("https://hogehoge.com/metadata/{id}.json") {
    }

    function setAllowlist(bytes32 listRoot) public onlyOwner {
        merkleRoot = listRoot;
    }

    modifier allowList(bytes32 merkleRoot, bytes32[] memory proof) {
        require(proof.verify(merkleRoot, keccak256(abi.encodePacked(msg.sender))),"You are not in the list");
        _;
    }

    function mint(uint256 amount) internal {
        _mint(msg.sender, 1, amount, "");
    }

    function allowlistMint(bytes32[] memory proof) public payable allowList(merkleRoot, proof) {
        mint(1);
    }

    function setURI(string memory uri_) public onlyOwner {
        _setURI(uri_);
    }
}

allowlistに存在するかどうかの検証は以下の部分で行っています。

    modifier allowList(bytes32 merkleRoot, bytes32[] memory proof) {
        require(proof.verify(merkleRoot, keccak256(abi.encodePacked(msg.sender))),"You are not in the list");
        _;
    }

mintを確認してみましょう。allowlistに登録されているwallet addressだとmintができて、登録されていないwallet adressだとmintができないことを確認してください。

mintするWebページを作る場合は、こちらが参考になります。

それでは、良いスマートコントラクト開発を!

参考

https://medium.com/codex/creating-an-nft-whitelist-using-merkle-tree-proofs-9668fbe72cb4

Discussion