Module 0x1::MerkleNFTDistributor
use 0x1::BCS;
use 0x1::Errors;
use 0x1::Hash;
use 0x1::MerkleProof;
use 0x1::NFT;
use 0x1::Signer;
use 0x1::Vector;
Resource MerkleNFTDistribution
struct MerkleNFTDistribution<NFTMeta: copy, drop, store> has key
Fields
-
merkle_root: vector<u8>
-
-
claimed_bitmap: vector<u128>
-
Constants
const ERR_NO_MINT_CAPABILITY: u64 = 1002;
const ALREADY_MINTED: u64 = 1000;
const INVALID_PROOF: u64 = 1001;
Function register
public fun register<NFTMeta: copy, drop, store, Info: copy, drop, store>(signer: &signer, merkle_root: vector<u8>, leafs: u64, info: Info, meta: NFT::Metadata): NFT::MintCapability<NFTMeta>
Implementation
public fun register<NFTMeta: copy + store + drop, Info: copy + store + drop>(signer: &signer, merkle_root: vector<u8>, leafs: u64, info: Info, meta: Metadata): MintCapability<NFTMeta> {
let bitmap_count = leafs / 128;
if (bitmap_count * 128 < leafs) {
bitmap_count = bitmap_count + 1;
};
let claimed_bitmap = Vector::empty();
let j = 0;
while (j < bitmap_count) {
Vector::push_back( &mut claimed_bitmap, 0u128);
j = j + 1;
};
let distribution = MerkleNFTDistribution<NFTMeta>{
merkle_root,
claimed_bitmap
};
NFT::register<NFTMeta, Info>(signer, info, meta);
move_to(signer, distribution);
NFT::remove_mint_capability<NFTMeta>(signer)
}
Function mint_with_cap
public fun mint_with_cap<NFTMeta: copy, drop, store, NFTBody: store, Info: copy, drop, store>(sender: &signer, cap: &mut NFT::MintCapability<NFTMeta>, creator: address, index: u64, base_meta: NFT::Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof: vector<vector<u8>>): NFT::NFT<NFTMeta, NFTBody>
Implementation
public fun mint_with_cap<NFTMeta: copy + store + drop, NFTBody: store, Info: copy + store + drop>(sender: &signer, cap:&mut MintCapability<NFTMeta>, creator: address, index: u64, base_meta: Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof:vector<vector<u8>>): NFT<NFTMeta, NFTBody>
acquires MerkleNFTDistribution {
let addr = Signer::address_of(sender);
let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
let minted = is_minted_<NFTMeta>(distribution, index);
assert!(!minted, Errors::custom(ALREADY_MINTED));
let leaf_data = encode_leaf(&index, &addr);
let verified = MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data));
assert!(verified, Errors::custom(INVALID_PROOF));
set_minted_(distribution, index);
let nft = NFT::mint_with_cap<NFTMeta, NFTBody, Info>(creator, cap, base_meta, type_meta, body);
return nft
}
Function encode_leaf
fun encode_leaf(index: &u64, account: &address): vector<u8>
Implementation
fun encode_leaf(index: &u64, account: &address): vector<u8> {
let leaf = Vector::empty();
Vector::append(&mut leaf, BCS::to_bytes(index));
Vector::append(&mut leaf, BCS::to_bytes(account));
leaf
}
Function set_minted_
fun set_minted_<NFTMeta: copy, drop, store>(distribution: &mut MerkleNFTDistributor::MerkleNFTDistribution<NFTMeta>, index: u64)
Implementation
fun set_minted_<NFTMeta: copy + store + drop>(distribution: &mut MerkleNFTDistribution<NFTMeta>, index: u64) {
let claimed_word_index = index / 128;
let claimed_bit_index = ((index % 128) as u8);
let word = Vector::borrow_mut(&mut distribution.claimed_bitmap, claimed_word_index);
// word | (1 << bit_index)
let mask = 1u128 << claimed_bit_index;
*word = (*word | mask);
}
Function verify_proof
public fun verify_proof<NFTMeta: copy, drop, store>(account: address, creator: address, index: u64, merkle_proof: vector<vector<u8>>): bool
Implementation
public fun verify_proof<NFTMeta: copy + store + drop>(account: address, creator: address, index: u64, merkle_proof:vector<vector<u8>>): bool
acquires MerkleNFTDistribution {
let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
let leaf_data = encode_leaf(&index, &account);
MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data))
}
Function is_minted
public fun is_minted<NFTMeta: copy, drop, store>(creator: address, index: u64): bool
Implementation
public fun is_minted<NFTMeta: copy + store + drop>(creator: address, index: u64): bool
acquires MerkleNFTDistribution {
let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
is_minted_<NFTMeta>(distribution, index)
}
Function is_minted_
fun is_minted_<NFTMeta: copy, drop, store>(distribution: &MerkleNFTDistributor::MerkleNFTDistribution<NFTMeta>, index: u64): bool
Implementation
fun is_minted_<NFTMeta: copy + store + drop>(distribution: &MerkleNFTDistribution<NFTMeta>, index: u64): bool {
let claimed_word_index = index / 128;
let claimed_bit_index = ((index % 128) as u8);
let word = Vector::borrow( &distribution.claimed_bitmap, claimed_word_index);
let mask = 1u128 << claimed_bit_index;
(*word & mask) == mask
}