Implementasikan minting eksklusif dengan whitelist berbasis Merkle tree di CreateKit
import { WhitelistManager } from '@b3dotfun/basemint'
// Mendefinisikan alamat yang masuk whitelist
const whitelistedAddresses = [
{ address: "0x1234567890123456789012345678901234567890" },
{ address: "0x2345678901234567890123456789012345678901" },
{ address: "0x3456789012345678901234567890123456789012" },
{ address: "0x4567890123456789012345678901234567890123" },
{ address: "0x5678901234567890123456789012345678901234" }
]
// Membuat manajer whitelist
const whitelist = new WhitelistManager(whitelistedAddresses)
// Mendapatkan akar Merkle untuk penempatan kontrak
const merkleRoot = whitelist.getRoot()
console.log(`Akar Merkle: ${merkleRoot}`)
// Memverifikasi whitelist dibangun dengan benar
console.log(`Whitelist berisi ${whitelistedAddresses.length} alamat`)
// Entri whitelist dapat mencakup metadata tambahan
const advancedWhitelist = [
{
address: "0x1234567890123456789012345678901234567890",
alokasi: 5, // Maks 5 token untuk alamat ini
tier: "gold"
},
{
address: "0x2345678901234567890123456789012345678901",
alokasi: 3,
tier: "silver"
},
{
address: "0x3456789012345678901234567890123456789012",
alokasi: 1,
tier: "bronze"
}
]
// Membuat whitelist (hanya alamat yang digunakan untuk Merkle tree)
const whitelist = new WhitelistManager(
advancedWhitelist.map(entry => ({ address: entry.address }))
)
// Menyimpan metadata secara terpisah untuk logika aplikasi
const allocationMap = new Map(
advancedWhitelist.map(entry => [entry.address, entry.allocation])
)
import { CollectionManager } from '@b3dotfun/basemint'
// Membuat koleksi dengan whitelist diaktifkan
const whitelistCollection = {
name: "Exclusive Collection",
symbol: "EXCL",
creator: account.address,
gameOwner: account.address,
// Konfigurasi whitelist
isWhitelistEnabled: true,
whitelistMerkleRoot: merkleRoot,
// Pengaturan koleksi
maxSupply: 1000n,
mintPrice: parseEther("0.01"),
maxPerWallet: 3n,
// Opsional: Gabungkan dengan akses berbasis waktu
startTime: BigInt(Math.floor(Date.now() / 1000) + 3600), // Whitelist dimulai dalam 1 jam
endTime: BigInt(Math.floor(Date.now() / 1000) + 86400 * 7), // Berakhir dalam 7 hari
}
// Menghasilkan tanda tangan dan penempatan
const creatorSignature = await collectionManager.generateCreatorSignature(
walletClient,
whitelistCollection
)
const predictedAddress = collectionManager.predictCollectionAddress(
whitelistCollection,
creatorSignature
)
console.log(`Koleksi whitelist akan ditempatkan di: ${predictedAddress}`)
// Membuat koleksi dengan fase akses yang berbeda
const phasedCollection = {
name: "Phased Access Collection",
symbol: "PAC",
creator: account.address,
gameOwner: account.address,
// Fase 1: Hanya whitelist (24 jam pertama)
isWhitelistEnabled: true,
whitelistMerkleRoot: merkleRoot,
startTime: BigInt(Math.floor(Date.now() / 1000)),
// Catatan: Untuk fase 2 (akses publik), Anda akan memerlukan logika tambahan
// untuk menonaktifkan pemeriksaan whitelist setelah waktu tertentu
}
// Dalam aplikasi Anda, implementasikan logika fase
async function checkMintingPhase(collection: any): Promise<'whitelist' | 'public' | 'ended'> {
const now = BigInt(Math.floor(Date.now() / 1000))
const startTime = await collection.startTime()
const endTime = await collection.endTime()
const whitelistPhaseEnd = startTime + 86400n // 24 jam
if (now < startTime) {
throw new Error("Minting belum dimulai")
} else if (now < whitelistPhaseEnd) {
return 'whitelist'
} else if (now < endTime) {
return 'public'
} else {
return 'ended'
}
}
// Menghasilkan bukti untuk alamat tertentu
function generateProofForAddress(whitelist: WhitelistManager, address: string): string[] {
try {
const proof = whitelist.getProof(address)
console.log(`Bukti dihasilkan untuk ${address}:`, proof)
return proof
} catch (error) {
console.error(`Gagal menghasilkan bukti untuk ${address}:`, error)
throw new Error(`Alamat ${address} tidak dalam whitelist`)
}
}
// Memverifikasi bukti secara lokal (pemeriksaan opsional)
function verifyWhitelistProof(
whitelist: WhitelistManager,
address: string,
proof: string[]
): boolean {
const isValid = whitelist.verify(address, proof)
console.log(`Verifikasi bukti untuk ${address}: ${isValid ? '✅ Valid' : '❌ Invalid'}`)
return isValid
}
// Contoh penggunaan
const userAddress = "0x1234567890123456789012345678901234567890"
const proof = generateProofForAddress(whitelist, userAddress)
const isValid = verifyWhitelistProof(whitelist, userAddress, proof)
// Menghasilkan bukti untuk beberapa alamat
function generateBatchProofs(
whitelist: WhitelistManager,
addresses: string[]
): Map<string, string[]> {
const proofMap = new Map<string, string[]>()
for (const address of addresses) {
try {
const proof = whitelist.getProof(address)
proofMap.set(address, proof)
console.log(`✅ Bukti dihasilkan untuk ${address}`)
} catch (error) {
console.error(`❌ Gagal menghasilkan bukti untuk ${address}:`, error)
}
}
return proofMap
}
// Menghasilkan bukti untuk semua alamat yang masuk whitelist
const allAddresses = whitelistedAddresses.map(entry => entry.address)
const allProofs = generateBatchProofs(whitelist, allAddresses)
// Menyimpan bukti ke file atau database untuk penggunaan frontend
const proofData = {
merkleRoot,
proofs: Object.fromEntries(allProofs)
}
// Contoh: Simpan ke file JSON
import { writeFileSync } from 'fs'
writeFileSync('whitelist-proofs.json', JSON.stringify(proofData, null, 2))
async function mintWithWhitelist(
collection: any,
walletClient: any,
whitelist: WhitelistManager,
quantity: bigint = 1n
) {
const userAddress = walletClient.account.address
try {
// Menghasilkan bukti untuk pengguna
const proof = whitelist.getProof(userAddress)
// Memverifikasi bukti secara lokal (opsional)
const isValid = whitelist.verify(userAddress, proof)
if (!isValid) {
throw new Error("Bukti whitelist tidak valid")
}
// Mendapatkan harga mint
const mintPrice = await collection.mintPrice()
const totalPrice = mintPrice * quantity
// Mint dengan bukti whitelist
const tx = await collection.mint(
walletClient,
quantity,
undefined, // URI metadata
totalPrice,
proof // Bukti whitelist
)
console.log(`✅ Mint whitelist berhasil: ${tx}`)
return tx
} catch (error: any) {
if (error.message.includes('Invalid merkle proof')) {
console.error('❌ Alamat tidak dalam whitelist atau bukti tidak valid')
} else {
console.error('❌ Mint whitelist gagal:', error)
}
throw error
}
}
// Penggunaan
await mintWithWhitelist(collection, walletClient, whitelist, 2n)
async function advancedWhitelistMint(
collection: any,
walletClient: any,
whitelist: WhitelistManager,
allocationMap: Map<string, number>,
quantity: bigint
) {
const userAddress = walletClient.account.address
// Memeriksa alokasi pengguna
const maxAllocation = allocationMap.get(userAddress) || 0
if (maxAllocation === 0) {
throw new Error("Alamat tidak dalam whitelist")
}
// Memeriksa saldo saat ini terhadap alokasi
const currentBalance = await collection.balanceOf(userAddress)
const newBalance = currentBalance + quantity
if (newBalance > BigInt(maxAllocation)) {
throw new Error(`Akan melebihi alokasi. Maks: ${maxAllocation}, Saat ini: ${currentBalance}`)
}
// Menghasilkan bukti dan mint
const proof = whitelist.getProof(userAddress)
const mintPrice = await collection.mintPrice()
const tx = await collection.mint(
walletClient,
quantity,
undefined,
mintPrice * quantity,
proof
)
console.log(`✅ Mint whitelist lanjutan berhasil: ${tx}`)
return tx
}
import { useState, useEffect } from 'react'
import { useAccount } from 'wagmi'
interface WhitelistStatus {
isWhitelisted: boolean
proof: string[] | null
allocation: number
loading: boolean
error: string | null
}
export function useWhitelistStatus(
whitelist: WhitelistManager,
allocationMap?: Map<string, number>
): WhitelistStatus {
const { address } = useAccount()
const [status, setStatus] = useState<WhitelistStatus>({
isWhitelisted: false,
proof: null,
allocation: 0,
loading: true,
error: null
})
useEffect(() => {
async function checkWhitelistStatus() {
if (!address) {
setStatus({
isWhitelisted: false,
proof: null,
allocation: 0,
loading: false,
error: null
})
return
}
try {
setStatus(prev => ({ ...prev, loading: true, error: null }))
// Menghasilkan bukti
const proof = whitelist.getProof(address)
const isValid = whitelist.verify(address, proof)
const allocation = allocationMap?.get(address) || 0
setStatus({
isWhitelisted: isValid,
proof: isValid ? proof : null,
allocation,
loading: false,
error: null
})
} catch (error: any) {
setStatus({
isWhitelisted: false,
proof: null,
allocation: 0,
loading: false,
error: error.message
})
}
}
checkWhitelistStatus()
}, [address, whitelist, allocationMap])
return status
}
import React from 'react'
import { useWhitelistStatus } from './useWhitelistStatus'
interface WhitelistStatusProps {
whitelist: WhitelistManager
allocationMap?: Map<string, number>
}
export function WhitelistStatus({ whitelist, allocationMap }: WhitelistStatusProps) {
const { isWhitelisted, allocation, loading, error } = useWhitelistStatus(
whitelist,
allocationMap
)
if (loading) {
return <div className="animate-pulse">Memeriksa status whitelist...</div>
}
if (error) {
return <div className="text-red-500">Error: {error}</div>
}
return (
<div className={`p-4 rounded-lg ${isWhitelisted ? 'bg-green-100' : 'bg-gray-100'}`}>
{isWhitelisted ? (
<div className="text-green-800">
<h3 className="font-bold">✅ Masuk Whitelist</h3>
<p>Anda dapat mint hingga {allocation} token</p>
</div>
) : (
<div className="text-gray-800">
<h3 className="font-bold">❌ Tidak Masuk Whitelist</h3>
<p>Alamat Anda tidak memenuhi syarat untuk minting whitelist</p>
</div>
)}
</div>
)
}
// Membuat tingkatan yang berbeda dengan manfaat yang berbeda
const goldTierAddresses = [
{ address: "0x1111..." },
{ address: "0x2222..." }
]
const silverTierAddresses = [
{ address: "0x3333..." },
{ address: "0x4444..." }
]
const bronzeTierAddresses = [
{ address: "0x5555..." },
{ address: "0x6666..." }
]
// Membuat whitelist terpisah untuk setiap tingkatan
const goldWhitelist = new WhitelistManager(goldTierAddresses)
const silverWhitelist = new WhitelistManager(silverTierAddresses)
const bronzeWhitelist = new WhitelistManager(bronzeTierAddresses)
// Untuk smart contract, Anda mungkin menggabungkan semua tingkatan
const allTierAddresses = [
...goldTierAddresses,
...silverTierAddresses,
...bronzeTierAddresses
]
const combinedWhitelist = new WhitelistManager(allTierAddresses)
// Logika aplikasi untuk manfaat tingkatan
const tierBenefits = {
gold: { maxMint: 10, diskon: 0.2 },
silver: { maxMint: 5, diskon: 0.1 },
bronze: { maxMint: 2, diskon: 0.05 }
}
function getTierForAddress(address: string): 'gold' | 'silver' | 'bronze' | null {
if (goldTierAddresses.some(entry => entry.address === address)) return 'gold'
if (silverTierAddresses.some(entry => entry.address === address)) return 'silver'
if (bronzeTierAddresses.some(entry => entry.address === address)) return 'bronze'
return null
}
async function getActiveTierForTime(timestamp: number): Promise<'gold' | 'silver' | 'bronze' | 'public' | null> {
const phaseStartTime = 1640995200 // Contoh timestamp
const hoursSinceStart = (timestamp - phaseStartTime) / 3600
if (hoursSinceStart < 0) return null // Belum dimulai
if (hoursSinceStart < 1) return 'gold' // Jam pertama: hanya tingkatan emas
if (hoursSinceStart < 4) return 'silver' // 3 jam berikutnya: emas + perak
if (hoursSinceStart < 24) return 'bronze' // 20 jam berikutnya: semua tingkatan
return 'public' // Setelah 24 jam: akses publik
}
async function mintWithTierAccess(
collection: any,
walletClient: any,
address: string,
quantity: bigint
) {
const now = Math.floor(Date.now() / 1000)
const activeTier = await getActiveTierForTime(now)
const userTier = getTierForAddress(address)
// Memeriksa apakah pengguna dapat mint dalam fase saat ini
if (activeTier === 'public') {
// Minting publik - tidak memerlukan whitelist
await collection.mint(walletClient, quantity, undefined, mintPrice * quantity, [])
} else