Open2
マークルツリーによるWLの実装
以下の素晴らしい記事と動画ついて、自分のメモをくっつけたもの
背景
NFTプロジェクトでホワイトリスト(WL)はほとんどのプロジェクトで採用されている。
ミンティングサイトにおいて、WLを持ってる人だけ早めにmintできるような実装をしたい。
簡単に思いつくのは、WLを持ってる人のアドレスをスマートコントラクトに登録しておくことだが、書き込むときのガス代がやばいのでなんとかしたい。
マークルツリー
詳細は上の記事が丁寧すぎるので割愛。
ちょっと情報をスマートコントラクトに与えてあげるだけで、WL持ちアドレスかどうかの判断ができる。
実装と構成
バックエンド
WL持ちのアドレスやマークルツリーの作成はバックエンド側で行った方が安全?
- WLを持ったアドレスを格納しておく
let whitelistAddresses = [
"0X5B38DA6A701C568545DCFCB03FCB875F56BEDDC4",
"0X5A641E5FB72A2FD9137312E7694D42996D689D99",
"0XDCAB482177A592E424D1C8318A464FC922E8DE40",
"0X6E21D37E07A6F7E53C7ACE372CEC63D4AE4B6BD0",
"0X09BAAB19FC77C19898140DADD30C4685C597620B",
"0XCC4C29997177253376528C05D3DF91CF2D69061A",
"0xdD870fA1b7C4700F2BD7f44238821C26f7392148", // The address in remix
];
- 上のアドレスをleafnodesとして設定。マークルツリーを作る。
const leafnodes = whitelistAddresses.map((addr) => keccak256(addr));
const merkleTree = new MerkleTree(leafnodes, keccak256, { sortPairs: true });
フロントエンド
任意のアドレスからのmint要求に対して、バックエンドと通信し証明に必要な情報を取得する
- 証明に必要なハッシュの取得(例えばleafのペアとか)
const hexProof = merkleTree.getHexProof(claimingAddress);
スマートコントラクト
rootHashが改竄されると終わりなので、これはスマートコントラクトに保持しておく
また、チェックを行う
下はjsのコードになってるけど、これで取得できたやつをスマコンにセットしようねということ
- rootHashの取得
const rootHash = merkleTree.getRoot();
- WL持ちのアドレスであればTrueが返される
merkleTree.verify(hexProof, claimingAddress, rootHash)
実例
KillerGFのコントラクトがこの方式を取っていた
rootHashの登録だと思われる function setPresaleRoots(bytes32 _whitelistRoot, bytes32 _uwulistRoot, bytes32 _teamRoot) external onlyOwner {
whitelistRoot = _whitelistRoot;
uwuRoot = _uwulistRoot;
teamRoot = _teamRoot;
}
販売開始前のトランザクション。なんで3回やってるかは謎。1回目と2回目は引数が違うので変更があったのかなと思うけど、2回目と3回目は同じに見える。。
こんな感じで購入時にチェックしてる
if (merkleProof[TEAM_INDEX].length != 0) {
require(teamRoot.length != 0, "team root not assigned");
bytes32 node = keccak256(abi.encodePacked(indexes[TEAM_INDEX], msg.sender, amounts[TEAM_INDEX]));
require(MerkleProof.verify(merkleProof[TEAM_INDEX], teamRoot, node), 'MerkleProof: Invalid team proof.');
require(amountsToBuy[TEAM_INDEX] <= amounts[TEAM_INDEX], "Cant buy this many");
count += amountsToBuy[TEAM_INDEX];
teamMinted += uint64(amountsToBuy[TEAM_INDEX]);
}
個数も制御したい時はこっち