限られた人だけがMintできるsmart contractを作成する
概要
最近では誰もがmintできるようにするpublic sale前に、限られた人(allow list)だけmintができるsmart contractをよくみかけます。
Merkle Treesを使って限られた人だけがmintできるSmart Contractを開発したのでその知見をまとめます
最初に
smart contractの基本的な書き方などはスキップします。
書いたことがない人は以下の記事を読んでください。めちゃくちゃわかりやすいです
参考
こちらの実装を参考にさせてもらいました!
contractの実装、web側での呼び出し方のサンプルが書いてあるのでとても参考になります。
ライブラリ
openzepplinのライブラリを使用します
やってみる
# 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ライブラリを使います。
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ページを作る場合は、こちらが参考になります。
それでは、良いスマートコントラクト開発を!
参考
Discussion