starcoin-framework

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
}