Complete implementation examples for common CreateKit use cases
import {
CollectionManager,
BaseMintStorage,
b3Testnet
} from '@b3dotfun/basemint'
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
async function createBasicArtCollection() {
// Setup clients
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const publicClient = createPublicClient({
chain: b3Testnet,
transport: http()
})
const walletClient = createWalletClient({
chain: b3Testnet,
transport: http(),
account
})
// Initialize services
const collectionManager = new CollectionManager(publicClient)
const storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
// Define collection
const artCollection = {
name: "Digital Art Gallery",
symbol: "DAG",
creator: account.address,
gameOwner: account.address,
description: "A curated collection of digital artworks",
image: "https://example.com/art-collection.png",
maxSupply: 1000n,
mintPrice: 0n, // Free minting
maxPerWallet: 5n,
tokenStandard: "ERC721" as const,
chainId: 1993
}
try {
console.log("🎨 Creating art collection...")
// Generate creator signature
const creatorSignature = await collectionManager.generateCreatorSignature(
walletClient,
artCollection
)
// Predict address
const predictedAddress = collectionManager.predictCollectionAddress(
artCollection,
creatorSignature
)
console.log(`📍 Collection address: ${predictedAddress}`)
// Submit to storage
await storage.submitCollection(artCollection, creatorSignature)
console.log("✅ Collection metadata stored")
// Deploy and mint first NFT
const deployerSignature = await collectionManager.generateDeployerSignature(
walletClient,
predictedAddress
)
const collection = collectionManager.createCollection(predictedAddress, "ERC721")
const mintTx = await collection.mint(
walletClient,
1n,
undefined,
0n,
[],
creatorSignature,
deployerSignature
)
console.log(`🎉 Collection deployed and first NFT minted: ${mintTx}`)
return { collection, predictedAddress, mintTx }
} catch (error) {
console.error("❌ Failed to create art collection:", error)
throw error
}
}
// Usage
createBasicArtCollection()
.then(result => console.log("Collection created successfully:", result))
.catch(error => console.error("Creation failed:", error))
Complete Gaming Collection Example
import {
CollectionManager,
WhitelistManager,
BaseMintStorage,
RewardTracker,
b3Testnet
} from '@b3dotfun/basemint'
import { createPublicClient, createWalletClient, http, parseEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
class GamingCollectionManager {
private publicClient: any
private walletClient: any
private collectionManager: CollectionManager
private storage: BaseMintStorage
private rewardTracker: RewardTracker
constructor(privateKey: string) {
const account = privateKeyToAccount(privateKey as `0x${string}`)
this.publicClient = createPublicClient({
chain: b3Testnet,
transport: http()
})
this.walletClient = createWalletClient({
chain: b3Testnet,
transport: http(),
account
})
this.collectionManager = new CollectionManager(this.publicClient)
this.storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
this.rewardTracker = new RewardTracker(this.publicClient)
}
async createGamingCollection() {
// Define player tiers
const playerTiers = {
legendary: [
"0x1111111111111111111111111111111111111111",
"0x2222222222222222222222222222222222222222"
],
epic: [
"0x3333333333333333333333333333333333333333",
"0x4444444444444444444444444444444444444444",
"0x5555555555555555555555555555555555555555"
],
rare: [
"0x6666666666666666666666666666666666666666",
"0x7777777777777777777777777777777777777777",
"0x8888888888888888888888888888888888888888"
]
}
// Create combined whitelist
const allPlayers = [
...playerTiers.legendary.map(address => ({ address })),
...playerTiers.epic.map(address => ({ address })),
...playerTiers.rare.map(address => ({ address }))
]
const whitelist = new WhitelistManager(allPlayers)
const merkleRoot = whitelist.getRoot()
// Define collection
const gamingCollection = {
name: "Legendary Gaming Items",
symbol: "LGI",
creator: this.walletClient.account.address,
gameOwner: "0x9999999999999999999999999999999999999999", // Game platform
description: "Exclusive gaming items for top players",
image: "https://example.com/gaming-collection.png",
// Whitelist configuration
isWhitelistEnabled: true,
whitelistMerkleRoot: merkleRoot,
// Pricing and limits
maxSupply: 500n,
mintPrice: parseEther("0.01"),
maxPerWallet: 3n,
// Timing - whitelist phase for 24 hours
startTime: BigInt(Math.floor(Date.now() / 1000)),
endTime: BigInt(Math.floor(Date.now() / 1000) + 86400 * 7),
tokenStandard: "ERC1155" as const,
chainId: 1993,
// Game-specific metadata
attributes: [
{ trait_type: "Category", value: "Gaming" },
{ trait_type: "Rarity", value: "Legendary" },
{ trait_type: "Game", value: "Fantasy RPG" }
]
}
try {
console.log("🎮 Creating gaming collection...")
// Generate signatures
const creatorSignature = await this.collectionManager.generateCreatorSignature(
this.walletClient,
gamingCollection
)
const predictedAddress = this.collectionManager.predictCollectionAddress(
gamingCollection,
creatorSignature
)
// Submit to storage with game referrer
await this.storage.submitCollection(
gamingCollection,
creatorSignature,
"fantasy-rpg-game" // Referrer ID
)
console.log(`✅ Gaming collection stored at: ${predictedAddress}`)
return {
collection: gamingCollection,
predictedAddress,
whitelist,
playerTiers
}
} catch (error) {
console.error("❌ Gaming collection creation failed:", error)
throw error
}
}
async mintForPlayer(
collectionAddress: string,
whitelist: WhitelistManager,
playerAddress: string,
quantity: bigint = 1n
) {
try {
// Check if collection is deployed
const collection = this.collectionManager.createCollection(collectionAddress, "ERC1155")
const isDeployed = await collection.isDeployed()
if (!isDeployed) {
// First mint - deploy collection
const deployerSignature = await this.collectionManager.generateDeployerSignature(
this.walletClient,
collectionAddress
)
// Get whitelist proof
const proof = whitelist.getProof(playerAddress)
const mintPrice = parseEther("0.01") * quantity
const tx = await collection.mint(
this.walletClient,
quantity,
"https://example.com/legendary-sword.json", // Specific item metadata
mintPrice,
proof,
undefined, // creatorSignature needed for deployment
deployerSignature
)
console.log(`🚀 Collection deployed and item minted: ${tx}`)
return tx
} else {
// Regular mint
const proof = whitelist.getProof(playerAddress)
const mintPrice = parseEther("0.01") * quantity
const tx = await collection.mint(
this.walletClient,
quantity,
"https://example.com/legendary-sword.json",
mintPrice,
proof
)
console.log(`⚔️ Gaming item minted: ${tx}`)
return tx
}
} catch (error: any) {
if (error.message.includes('Invalid merkle proof')) {
console.error(`❌ Player ${playerAddress} not in whitelist`)
} else {
console.error("❌ Minting failed:", error)
}
throw error
}
}
async getPlayerRewards(collectionAddress: string, playerAddress: string) {
const escrowAddress = this.collectionManager.getEscrowAddress()
// Check if player was first minter
const firstMinterRewards = await this.rewardTracker.getRecipientRewards(
escrowAddress,
collectionAddress,
"FIRST_MINTER",
playerAddress
)
// Get collection stats
const collectionRewards = await this.rewardTracker.getCollectionRewards(
escrowAddress,
collectionAddress
)
return {
firstMinterRewards: firstMinterRewards.toString(),
collectionTotalRewards: collectionRewards.totalRewards.toString(),
collectionMints: collectionRewards.totalMints.toString()
}
}
}
// Usage example
async function main() {
const gameManager = new GamingCollectionManager(process.env.PRIVATE_KEY!)
// Create collection
const { predictedAddress, whitelist, playerTiers } = await gameManager.createGamingCollection()
// Simulate legendary player minting
const legendaryPlayer = playerTiers.legendary[0]
await gameManager.mintForPlayer(predictedAddress, whitelist, legendaryPlayer, 2n)
// Check rewards
const rewards = await gameManager.getPlayerRewards(predictedAddress, legendaryPlayer)
console.log("Player rewards:", rewards)
}
main().catch(console.error)
Multi-Collection Platform Example
import {
CollectionManager,
BaseMintStorage,
RewardTracker,
b3Testnet
} from '@b3dotfun/basemint'
class NFTPlatform {
private collectionManager: CollectionManager
private storage: BaseMintStorage
private rewardTracker: RewardTracker
private platformId: string
constructor(
publicClient: any,
platformId: string
) {
this.collectionManager = new CollectionManager(publicClient)
this.storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
this.rewardTracker = new RewardTracker(publicClient)
this.platformId = platformId
}
async registerPlatform() {
try {
await this.storage.registerReferrer(this.platformId, {
name: "My NFT Platform",
website: "https://mynftplatform.com",
description: "A platform for creating and managing NFT collections"
})
console.log(`✅ Platform registered: ${this.platformId}`)
} catch (error: any) {
if (error.message.includes('already exists')) {
console.log(`ℹ️ Platform ${this.platformId} already registered`)
} else {
throw error
}
}
}
async createCollection(
creatorWalletClient: any,
collectionData: {
name: string
symbol: string
description: string
image: string
maxSupply: bigint
mintPrice: bigint
category: string
}
) {
const collection = {
...collectionData,
creator: creatorWalletClient.account.address,
gameOwner: creatorWalletClient.account.address, // Platform could be gameOwner
tokenStandard: "ERC721" as const,
chainId: 1993,
attributes: [
{ trait_type: "Platform", value: "My NFT Platform" },
{ trait_type: "Category", value: collectionData.category }
]
}
// Generate signature and store
const creatorSignature = await this.collectionManager.generateCreatorSignature(
creatorWalletClient,
collection
)
const predictedAddress = this.collectionManager.predictCollectionAddress(
collection,
creatorSignature
)
await this.storage.submitCollection(
collection,
creatorSignature,
this.platformId
)
return {
collection,
predictedAddress,
creatorSignature
}
}
async deployCollection(
deployerWalletClient: any,
collectionAddress: string,
creatorSignature: string
) {
const deployerSignature = await this.collectionManager.generateDeployerSignature(
deployerWalletClient,
collectionAddress
)
const collection = this.collectionManager.createCollection(collectionAddress, "ERC721")
const tx = await collection.mint(
deployerWalletClient,
1n,
undefined,
0n, // Deployer gets first mint free
[],
creatorSignature,
deployerSignature
)
return tx
}
async getPlatformStats() {
const collections = await this.storage.queryCollections({
referrer: this.platformId
})
const stats = {
totalCollections: collections.total,
deployed: 0,
undeployed: 0,
totalVolume: 0n,
totalRewards: 0n
}
const escrowAddress = this.collectionManager.getEscrowAddress()
for (const collection of collections.collections) {
if (collection.isDeployed) {
stats.deployed++
// Get collection rewards
const rewards = await this.rewardTracker.getCollectionRewards(
escrowAddress,
collection.address
)
stats.totalRewards += rewards.totalRewards
} else {
stats.undeployed++
}
}
return {
...stats,
collections: collections.collections
}
}
async getCreatorDashboard(creatorAddress: string) {
const collections = await this.storage.queryCollections({
creator: creatorAddress,
referrer: this.platformId
})
const escrowAddress = this.collectionManager.getEscrowAddress()
let totalCreatorRewards = 0n
const collectionStats = await Promise.all(
collections.collections.map(async (collection) => {
if (collection.isDeployed) {
const rewards = await this.rewardTracker.getRecipientRewards(
escrowAddress,
collection.address,
"CREATOR",
creatorAddress
)
totalCreatorRewards += rewards
return {
...collection,
creatorRewards: rewards.toString()
}
}
return {
...collection,
creatorRewards: "0"
}
})
)
return {
totalCollections: collections.total,
totalCreatorRewards: totalCreatorRewards.toString(),
collections: collectionStats
}
}
}
// Usage
async function runPlatform() {
const publicClient = createPublicClient({
chain: b3Testnet,
transport: http()
})
const platform = new NFTPlatform(publicClient, "my-nft-platform")
// Register platform
await platform.registerPlatform()
// Creator creates collection
const creatorAccount = privateKeyToAccount(process.env.CREATOR_PRIVATE_KEY as `0x${string}`)
const creatorWalletClient = createWalletClient({
chain: b3Testnet,
transport: http(),
account: creatorAccount
})
const { predictedAddress, creatorSignature } = await platform.createCollection(
creatorWalletClient,
{
name: "Awesome Art Collection",
symbol: "AAC",
description: "Amazing digital artworks",
image: "https://example.com/awesome-art.png",
maxSupply: 1000n,
mintPrice: parseEther("0.005"),
category: "Art"
}
)
// Deploy collection
const deployerAccount = privateKeyToAccount(process.env.DEPLOYER_PRIVATE_KEY as `0x${string}`)
const deployerWalletClient = createWalletClient({
chain: b3Testnet,
transport: http(),
account: deployerAccount
})
const deployTx = await platform.deployCollection(
deployerWalletClient,
predictedAddress,
creatorSignature
)
console.log(`🚀 Collection deployed: ${deployTx}`)
// Get platform statistics
const stats = await platform.getPlatformStats()
console.log("Platform stats:", stats)
// Get creator dashboard
const dashboard = await platform.getCreatorDashboard(creatorAccount.address)
console.log("Creator dashboard:", dashboard)
}
runPlatform().catch(console.error)
import {
CollectionManager,
BaseMintStorage,
RewardTracker
} from '@b3dotfun/basemint'
class NFTMarketplace {
private storage: BaseMintStorage
private collectionManager: CollectionManager
private rewardTracker: RewardTracker
constructor(publicClient: any) {
this.storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
this.collectionManager = new CollectionManager(publicClient)
this.rewardTracker = new RewardTracker(publicClient)
}
// Discover collections for marketplace display
async discoverCollections(filters?: {
category?: string
minSupply?: number
maxPrice?: string
deployed?: boolean
}) {
const queryFilters: any = {}
if (filters?.deployed !== undefined) {
queryFilters.deployed = filters.deployed
}
const collections = await this.storage.queryCollections({
...queryFilters,
limit: 50,
sortBy: "createdAt",
sortOrder: "desc"
})
// Enrich with on-chain data for deployed collections
const enrichedCollections = await Promise.all(
collections.collections.map(async (collection) => {
if (collection.isDeployed) {
const contract = this.collectionManager.createCollection(
collection.address,
collection.tokenStandard
)
const [totalSupply, mintPrice, maxSupply] = await Promise.all([
contract.totalSupply(),
contract.mintPrice(),
contract.maxSupply()
])
return {
...collection,
onChainData: {
totalSupply: totalSupply.toString(),
mintPrice: mintPrice.toString(),
maxSupply: maxSupply.toString(),
remainingSupply: (maxSupply - totalSupply).toString()
}
}
}
return collection
})
)
// Apply additional filters
let filtered = enrichedCollections
if (filters?.category) {
filtered = filtered.filter(c =>
c.attributes?.some(attr =>
attr.trait_type === "Category" &&
attr.value.toLowerCase().includes(filters.category!.toLowerCase())
)
)
}
if (filters?.minSupply) {
filtered = filtered.filter(c =>
!c.onChainData ||
parseInt(c.onChainData.totalSupply) >= filters.minSupply!
)
}
return filtered
}
// Get trending collections
async getTrendingCollections(timeframe: '24h' | '7d' | '30d' = '24h') {
const collections = await this.storage.queryCollections({
deployed: true,
limit: 100
})
const escrowAddress = this.collectionManager.getEscrowAddress()
// Get reward data to determine trending status
const collectionsWithMetrics = await Promise.all(
collections.collections.map(async (collection) => {
const rewards = await this.rewardTracker.getCollectionRewards(
escrowAddress,
collection.address
)
return {
...collection,
metrics: {
totalMints: parseInt(rewards.totalMints.toString()),
totalRewards: rewards.totalRewards.toString(),
// Calculate trend score based on mints and rewards
trendScore: parseInt(rewards.totalMints.toString()) *
parseInt(rewards.totalRewards.toString())
}
}
})
)
// Sort by trend score
return collectionsWithMetrics
.sort((a, b) => b.metrics.trendScore - a.metrics.trendScore)
.slice(0, 10)
}
// Get collection details for marketplace display
async getCollectionDetails(address: string) {
try {
// Get metadata from storage
const collection = await this.storage.getCollection(address)
if (!collection.isDeployed) {
return {
...collection,
status: 'pending-deployment',
onChainData: null
}
}
// Get on-chain data
const contract = this.collectionManager.createCollection(
address,
collection.tokenStandard
)
const [info, isActive] = await Promise.all([
contract.getCollectionInfo(),
contract.isMintingActive()
])
// Get reward metrics
const escrowAddress = this.collectionManager.getEscrowAddress()
const rewards = await this.rewardTracker.getCollectionRewards(
escrowAddress,
address
)
return {
...collection,
status: 'deployed',
onChainData: {
name: info.name,
symbol: info.symbol,
totalSupply: info.totalSupply.toString(),
maxSupply: info.maxSupply.toString(),
mintPrice: info.mintPrice.toString(),
maxPerWallet: info.maxPerWallet.toString(),
isMintingActive: isActive,
remainingSupply: (info.maxSupply - info.totalSupply).toString()
},
metrics: {
totalMints: parseInt(rewards.totalMints.toString()),
totalRewards: rewards.totalRewards.toString(),
averageRewardPerMint: rewards.totalMints > 0n
? (rewards.totalRewards / rewards.totalMints).toString()
: "0"
}
}
} catch (error) {
console.error(`Failed to get collection details for ${address}:`, error)
throw error
}
}
// Search collections
async searchCollections(query: string) {
const searchResults = await this.storage.searchCollections({
query,
limit: 20
})
return searchResults.collections
}
// Get collections by creator for profile pages
async getCreatorCollections(creatorAddress: string) {
const collections = await this.storage.queryCollections({
creator: creatorAddress
})
const escrowAddress = this.collectionManager.getEscrowAddress()
let totalCreatorRewards = 0n
const enriched = await Promise.all(
collections.collections.map(async (collection) => {
if (collection.isDeployed) {
const rewards = await this.rewardTracker.getRecipientRewards(
escrowAddress,
collection.address,
"CREATOR",
creatorAddress
)
totalCreatorRewards += rewards
return {
...collection,
creatorRewards: rewards.toString()
}
}
return {
...collection,
creatorRewards: "0"
}
})
)
return {
collections: enriched,
totalCreatorRewards: totalCreatorRewards.toString(),
stats: {
total: collections.total,
deployed: enriched.filter(c => c.isDeployed).length,
pending: enriched.filter(c => !c.isDeployed).length
}
}
}
}
// Usage in marketplace
async function marketplaceDemo() {
const publicClient = createPublicClient({
chain: b3Testnet,
transport: http()
})
const marketplace = new NFTMarketplace(publicClient)
// Get homepage collections
const featuredCollections = await marketplace.discoverCollections({
deployed: true
})
console.log("Featured collections:", featuredCollections.slice(0, 6))
// Get trending collections
const trending = await marketplace.getTrendingCollections('7d')
console.log("Trending collections:", trending)
// Search functionality
const searchResults = await marketplace.searchCollections("art")
console.log("Search results for 'art':", searchResults)
// Get specific collection details
if (featuredCollections.length > 0) {
const details = await marketplace.getCollectionDetails(
featuredCollections[0].address
)
console.log("Collection details:", details)
}
}
marketplaceDemo().catch(console.error)
React Components Example
import React, { useState, useEffect } from 'react'
import { useAccount, useWalletClient } from 'wagmi'
import {
CollectionManager,
BaseMintStorage,
WhitelistManager
} from '@b3dotfun/basemint'
// Collection creation form component
export function CreateCollectionForm() {
const { address } = useAccount()
const { data: walletClient } = useWalletClient()
const [formData, setFormData] = useState({
name: '',
symbol: '',
description: '',
image: '',
maxSupply: '1000',
mintPrice: '0',
maxPerWallet: '5'
})
const [loading, setLoading] = useState(false)
const [result, setResult] = useState<any>(null)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!walletClient || !address) return
setLoading(true)
try {
const collectionManager = new CollectionManager(walletClient.chain)
const storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
const collection = {
name: formData.name,
symbol: formData.symbol,
creator: address,
gameOwner: address,
description: formData.description,
image: formData.image,
maxSupply: BigInt(formData.maxSupply),
mintPrice: BigInt(formData.mintPrice),
maxPerWallet: BigInt(formData.maxPerWallet),
tokenStandard: "ERC721" as const,
chainId: walletClient.chain.id
}
const signature = await collectionManager.generateCreatorSignature(
walletClient,
collection
)
const predictedAddress = collectionManager.predictCollectionAddress(
collection,
signature
)
await storage.submitCollection(collection, signature)
setResult({
collection,
predictedAddress,
signature
})
} catch (error: any) {
console.error('Collection creation failed:', error)
alert(`Failed to create collection: ${error.message}`)
} finally {
setLoading(false)
}
}
return (
<div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-6">Create NFT Collection</h2>
{result ? (
<div className="space-y-4">
<div className="p-4 bg-green-100 rounded-lg">
<h3 className="font-bold text-green-800">✅ Collection Created!</h3>
<p className="text-sm text-green-600">
Address: {result.predictedAddress}
</p>
</div>
<button
onClick={() => setResult(null)}
className="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Create Another Collection
</button>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1">Name</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Symbol</label>
<input
type="text"
value={formData.symbol}
onChange={(e) => setFormData({...formData, symbol: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Description</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({...formData, description: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
rows={3}
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Image URL</label>
<input
type="url"
value={formData.image}
onChange={(e) => setFormData({...formData, image: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium mb-1">Max Supply</label>
<input
type="number"
value={formData.maxSupply}
onChange={(e) => setFormData({...formData, maxSupply: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
min="1"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Mint Price (wei)</label>
<input
type="number"
value={formData.mintPrice}
onChange={(e) => setFormData({...formData, mintPrice: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
min="0"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Max Per Wallet</label>
<input
type="number"
value={formData.maxPerWallet}
onChange={(e) => setFormData({...formData, maxPerWallet: e.target.value})}
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500"
min="1"
/>
</div>
</div>
<button
type="submit"
disabled={loading || !address}
className="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
>
{loading ? 'Creating...' : 'Create Collection'}
</button>
</form>
)}
</div>
)
}
// Mint button component
export function MintButton({
collectionAddress,
tokenStandard,
quantity = 1n,
whitelistProof = [],
onSuccess,
onError
}: {
collectionAddress: string
tokenStandard: "ERC721" | "ERC1155"
quantity?: bigint
whitelistProof?: string[]
onSuccess?: (txHash: string) => void
onError?: (error: Error) => void
}) {
const { address } = useAccount()
const { data: walletClient } = useWalletClient()
const [loading, setLoading] = useState(false)
const [collectionInfo, setCollectionInfo] = useState<any>(null)
useEffect(() => {
async function loadCollectionInfo() {
if (!walletClient) return
try {
const collectionManager = new CollectionManager(walletClient.chain)
const collection = collectionManager.createCollection(collectionAddress, tokenStandard)
const [info, isActive, userBalance] = await Promise.all([
collection.getCollectionInfo(),
collection.isMintingActive(),
collection.balanceOf(address!)
])
setCollectionInfo({
...info,
isMintingActive: isActive,
userBalance: userBalance.toString(),
totalPrice: (info.mintPrice * quantity).toString()
})
} catch (error) {
console.error('Failed to load collection info:', error)
}
}
if (address && walletClient) {
loadCollectionInfo()
}
}, [address, walletClient, collectionAddress, tokenStandard, quantity])
const handleMint = async () => {
if (!walletClient || !address || !collectionInfo) return
setLoading(true)
try {
const collectionManager = new CollectionManager(walletClient.chain)
const collection = collectionManager.createCollection(collectionAddress, tokenStandard)
const tx = await collection.mint(
walletClient,
quantity,
undefined, // metadata URI
BigInt(collectionInfo.totalPrice),
whitelistProof
)
onSuccess?.(tx)
} catch (error: any) {
console.error('Mint failed:', error)
onError?.(error)
} finally {
setLoading(false)
}
}
if (!collectionInfo) {
return <div className="animate-pulse">Loading...</div>
}
const canMint = collectionInfo.isMintingActive &&
BigInt(collectionInfo.userBalance) + quantity <= BigInt(collectionInfo.maxPerWallet)
return (
<div className="p-4 border rounded-lg">
<div className="space-y-2 mb-4">
<h3 className="font-bold">{collectionInfo.name}</h3>
<p className="text-sm text-gray-600">
Price: {collectionInfo.mintPrice.toString()} wei × {quantity.toString()} = {collectionInfo.totalPrice} wei
</p>
<p className="text-sm text-gray-600">
Your balance: {collectionInfo.userBalance} / {collectionInfo.maxPerWallet.toString()}
</p>
<p className="text-sm text-gray-600">
Supply: {collectionInfo.totalSupply.toString()} / {collectionInfo.maxSupply.toString()}
</p>
</div>
<button
onClick={handleMint}
disabled={loading || !canMint || !address}
className="w-full py-2 px-4 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50"
>
{loading ? 'Minting...' : `Mint ${quantity} NFT${quantity > 1n ? 's' : ''}`}
</button>
{!canMint && collectionInfo.isMintingActive && (
<p className="text-sm text-red-600 mt-2">
Would exceed wallet limit
</p>
)}
{!collectionInfo.isMintingActive && (
<p className="text-sm text-red-600 mt-2">
Minting is not currently active
</p>
)}
</div>
)
}
// Collection gallery component
export function CollectionGallery() {
const [collections, setCollections] = useState<any[]>([])
const [loading, setLoading] = useState(true)
useEffect(() => {
async function loadCollections() {
try {
const storage = new BaseMintStorage({ baseUrl: 'https://api.basemint.fun' })
const result = await storage.queryCollections({
deployed: true,
limit: 12
})
setCollections(result.collections)
} catch (error) {
console.error('Failed to load collections:', error)
} finally {
setLoading(false)
}
}
loadCollections()
}, [])
if (loading) {
return <div className="text-center py-8">Loading collections...</div>
}
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{collections.map((collection) => (
<div key={collection.address} className="border rounded-lg overflow-hidden shadow-md">
{collection.image && (
<img
src={collection.image}
alt={collection.name}
className="w-full h-48 object-cover"
/>
)}
<div className="p-4">
<h3 className="font-bold text-lg">{collection.name}</h3>
<p className="text-gray-600 text-sm mb-2">{collection.symbol}</p>
<p className="text-gray-700 text-sm mb-4">{collection.description}</p>
<div className="space-y-1 text-xs text-gray-500">
<p>Creator: {collection.creator.slice(0, 6)}...{collection.creator.slice(-4)}</p>
<p>Standard: {collection.tokenStandard}</p>
<p>Chain: {collection.chainId}</p>
</div>
<MintButton
collectionAddress={collection.address}
tokenStandard={collection.tokenStandard}
onSuccess={(tx) => {
console.log('Mint successful:', tx)
alert('Mint successful!')
}}
onError={(error) => {
console.error('Mint failed:', error)
alert(`Mint failed: ${error.message}`)
}}
/>
</div>
</div>
))}
</div>
)
}
Testing Examples
import { describe, it, expect, beforeEach } from 'vitest'
import {
CollectionManager,
WhitelistManager,
BaseMintStorage
} from '@b3dotfun/basemint'
import { createTestClient, http, parseEther } from 'viem'
import { foundry } from 'viem/chains'
describe('CreateKit Integration Tests', () => {
let collectionManager: CollectionManager
let storage: BaseMintStorage
let testClient: any
let testAccount: any
beforeEach(() => {
testClient = createTestClient({
chain: foundry,
transport: http()
})
collectionManager = new CollectionManager(testClient)
storage = new BaseMintStorage({ baseUrl: 'http://localhost:3001' }) // Test server
testAccount = {
address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" as const
}
})
describe('Collection Creation', () => {
it('should create a basic collection', async () => {
const collection = {
name: "Test Collection",
symbol: "TEST",
creator: testAccount.address,
gameOwner: testAccount.address,
description: "A test collection",
maxSupply: 100n,
mintPrice: 0n,
tokenStandard: "ERC721" as const,
chainId: 31337
}
// Mock wallet client for testing
const mockWalletClient = {
account: testAccount,
signMessage: async () => "0x" + "00".repeat(65) // Mock signature
}
const signature = await collectionManager.generateCreatorSignature(
mockWalletClient as any,
collection
)
expect(signature).toBeDefined()
expect(signature.startsWith('0x')).toBe(true)
const predictedAddress = collectionManager.predictCollectionAddress(
collection,
signature
)
expect(predictedAddress).toBeDefined()
expect(predictedAddress.startsWith('0x')).toBe(true)
})
it('should handle invalid collection parameters', async () => {
const invalidCollection = {
name: "", // Invalid: empty name
symbol: "TEST",
creator: testAccount.address,
gameOwner: testAccount.address
}
const mockWalletClient = {
account: testAccount,
signMessage: async () => "0x" + "00".repeat(65)
}
await expect(
collectionManager.generateCreatorSignature(
mockWalletClient as any,
invalidCollection as any
)
).rejects.toThrow()
})
})
describe('Whitelist Management', () => {
it('should create and manage whitelists', () => {
const addresses = [
{ address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" },
{ address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" },
{ address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" }
]
const whitelist = new WhitelistManager(addresses)
const root = whitelist.getRoot()
expect(root).toBeDefined()
expect(root.startsWith('0x')).toBe(true)
// Test proof generation
const proof = whitelist.getProof(addresses[0].address)
expect(Array.isArray(proof)).toBe(true)
// Test verification
const isValid = whitelist.verify(addresses[0].address, proof)
expect(isValid).toBe(true)
// Test invalid address
expect(() => {
whitelist.getProof("0x0000000000000000000000000000000000000000")
}).toThrow()
})
it('should handle duplicate addresses', () => {
const addressesWithDuplicates = [
{ address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" },
{ address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" }, // Duplicate
{ address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" }
]
// Should handle duplicates gracefully
expect(() => {
new WhitelistManager(addressesWithDuplicates)
}).not.toThrow()
})
})
describe('Storage Integration', () => {
it('should submit collection to storage', async () => {
const collection = {
name: "Storage Test Collection",
symbol: "STC",
creator: testAccount.address,
gameOwner: testAccount.address,
description: "Testing storage integration",
tokenStandard: "ERC721" as const,
chainId: 31337
}
const mockSignature = "0x" + "00".repeat(65)
// Mock the storage submission
const mockResponse = {
collectionId: "test-id-123",
predictedAddress: "0x1234567890123456789012345678901234567890",
metadataUri: "https://api.basemint.fun/metadata/test-id-123"
}
// Note: In real tests, you'd mock the HTTP request
// For this example, we'll just test the function signature
expect(async () => {
await storage.submitCollection(collection, mockSignature)
}).not.toThrow()
})
})
describe('Error Handling', () => {
it('should handle network errors gracefully', async () => {
const collection = {
name: "Error Test Collection",
symbol: "ETC",
creator: testAccount.address,
gameOwner: testAccount.address,
tokenStandard: "ERC721" as const,
chainId: 31337
}
const mockWalletClient = {
account: testAccount,
signMessage: async () => {
throw new Error("Network error")
}
}
await expect(
collectionManager.generateCreatorSignature(
mockWalletClient as any,
collection
)
).rejects.toThrow("Network error")
})
it('should validate addresses', () => {
const invalidAddresses = [
"", // Empty
"0x123", // Too short
"0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", // Invalid characters
"1234567890123456789012345678901234567890" // Missing 0x prefix
]
invalidAddresses.forEach(address => {
expect(() => {
new WhitelistManager([{ address }])
}).toThrow()
})
})
})
describe('Performance Tests', () => {
it('should handle large whitelists efficiently', () => {
// Generate 10,000 test addresses
const largeAddressList = Array.from({ length: 10000 }, (_, i) => ({
address: `0x${i.toString(16).padStart(40, '0')}`
}))
const startTime = Date.now()
const whitelist = new WhitelistManager(largeAddressList)
const root = whitelist.getRoot()
const endTime = Date.now()
expect(root).toBeDefined()
expect(endTime - startTime).toBeLessThan(5000) // Should complete in under 5 seconds
// Test proof generation performance
const proofStartTime = Date.now()
const proof = whitelist.getProof(largeAddressList[0].address)
const proofEndTime = Date.now()
expect(proof).toBeDefined()
expect(proofEndTime - proofStartTime).toBeLessThan(1000) // Should complete in under 1 second
})
})
})
// Integration test helper functions
export class TestHelpers {
static generateRandomAddress(): string {
const randomBytes = Array.from({ length: 20 }, () =>
Math.floor(Math.random() * 256).toString(16).padStart(2, '0')
).join('')
return `0x${randomBytes}`
}
static generateTestCollection(overrides: any = {}) {
return {
name: "Test Collection",
symbol: "TEST",
creator: this.generateRandomAddress(),
gameOwner: this.generateRandomAddress(),
description: "A test collection for automated testing",
image: "https://example.com/test-image.png",
maxSupply: 1000n,
mintPrice: parseEther("0.001"),
maxPerWallet: 5n,
tokenStandard: "ERC721" as const,
chainId: 31337,
...overrides
}
}
static generateTestWhitelist(size: number = 10) {
return Array.from({ length: size }, () => ({
address: this.generateRandomAddress()
}))
}
static async waitForTransaction(txHash: string, client: any) {
let receipt = null
let attempts = 0
const maxAttempts = 30
while (!receipt && attempts < maxAttempts) {
try {
receipt = await client.getTransactionReceipt({ hash: txHash })
} catch {
// Transaction not yet mined
}
if (!receipt) {
await new Promise(resolve => setTimeout(resolve, 1000))
attempts++
}
}
if (!receipt) {
throw new Error(`Transaction ${txHash} not mined after ${maxAttempts} attempts`)
}
return receipt
}
}
// Example test runner script
async function runTests() {
console.log('Running CreateKit integration tests...')
try {
// Run the test suite
// This would typically be handled by your test runner (Jest, Vitest, etc.)
console.log('✅ All tests passed!')
} catch (error) {
console.error('❌ Tests failed:', error)
process.exit(1)
}
}
// Uncomment to run tests
// runTests()