NFTのmintサイトでは各ALがあり、それぞれのwalletで購入枚数が違うようなNFTのmintをすることがあります。
今回はその場合のフロントとスマートコントラクト(solidity)の書き方の説明します。
スマートコントラクト内にALの全リストを持つとスマコン自体が長くなるので、あまりスマートな状態になりません。そのため、Merkleツリーを利用しrootのhex値だけをスマートコントラクトに保存し、各データはスマコン内には持たないのが良い実装になるのでは無いかと思います。
事前準備
まず、事前に下記のようなwalletアドレスと最大購入枚数のデータを準備します。
export const allow_list = [
['0xxxx1', 2],
['0xxxx2', 4].....
上記のデータをリーフとしたMerkleツリーデータを作成し、そのルートのhex値を取得します。そしてこの値はスマートコントラクトでリーフの値が存在するかを確認するために、スマートコントラクト内に保存します。
const leaves = allow_list.map((addr) =>
ethers.utils.solidityKeccak256(['address', 'uint16'], [addr[0], addr[1]]),
)
const merkleTree = new MerkleTree(leaves, keccak256, { sort: true })
const hexRoot = merkleTree.getHexRoot()
フロントエンド
ALのミントする場合は、allow_listの配列がら、ミントする人のwalletアドレスがあるかをまず確認し、存在する場合にleafのデータを作成します。
const proofOfLeaf = merkleTree.getHexProof(
ethers.utils.solidityKeccak256(
['address', 'uint16'],
[allow_list[x][0], allow_list[x][1]],
)
)
このリーフの証明と最大購入枚数をmint時にスマートコンストラクタに送付します。
const preMintResult = await contract.alMint(
quantity,
proofOfLeaf,
allow_list[x][1],
{ value: 10000 },
)
スマートコントラクト
スマートコントラクトのmint時の関数の内部で、リーフを取得し、設定されているルートと照合し、verifiyできるかを確認します。
function alMint(
uint256 quantity,
bytes32[] memory proof,
uint256 max
) public payable {
require(
MerkleProof.verify(
proof,
hexedRoot,
keccak256(abi.encodePacked(msg.sender, max))
), "You are not in AL");
_safeMint(msg.sender, quantity);
}
※コードは簡略化されたものになりますので実際にはチェックなどを適切に追加してください。
追記:
多くのwallet addressは大文字小文字は認識しませんが、hash値を計算するとき等は大文字小文字を区別します。なので、すべてのwallet addressを小文字(もしくは大文字)で統一するか、データが混在しても大丈夫なようにコードでデータをreadするときに小文字にするようなコードをいれることをお勧めします。