Rewards System

Understanding and implementing the CreateKit reward distribution system

Overview

CreateKit features an innovative reward system that distributes a portion of each mint's value to various participants in the ecosystem. This creates aligned incentives and ensures all contributors benefit from successful collections.

Reward Structure

Default Distribution

Recipient TypeDefault ShareDescription
Creator40%Collection originator who created the collection
First Minter30%User who triggers the initial deployment and first mint
Game Owner20%Platform or game that integrates the collection
Platform10%BaseMint protocol fee
Note

Reward rates are configurable by the protocol administrator and may vary between deployments.

How Rewards Work

  1. Mint Payment: User pays the mint price for tokens
  2. Reward Calculation: A percentage of the payment is allocated to the reward pool
  3. Distribution: Rewards are distributed according to the configured rates
  4. Accumulation: Rewards accumulate in the escrow contract until claimed

Reward Tracking

Setting Up Reward Tracking

typescript
import { RewardTracker } from '@b3dotfun/basemint' import { createPublicClient, http } from 'viem' import { b3Testnet } from '@b3dotfun/basemint' const publicClient = createPublicClient({ chain: b3Testnet, transport: http() }) const rewardTracker = new RewardTracker(publicClient) // Get escrow contract address const escrowAddress = rewardTracker.getEscrowAddress() console.log(`Escrow contract: ${escrowAddress}`)

Collection-Level Rewards

Track rewards accumulated for an entire collection:

typescript
// Get total rewards for a collection const collectionRewards = await rewardTracker.getCollectionRewards( escrowAddress, collectionAddress ) console.log("Collection Rewards:", { totalRewards: collectionRewards.totalRewards.toString(), unclaimedRewards: collectionRewards.unclaimedRewards.toString(), totalMints: collectionRewards.totalMints.toString(), averageRewardPerMint: ( collectionRewards.totalRewards / collectionRewards.totalMints ).toString() })

Individual Recipient Rewards

Track rewards for specific participants:

typescript
// Get creator rewards for a collection const creatorRewards = await rewardTracker.getRecipientRewards( escrowAddress, collectionAddress, "CREATOR", creatorAddress ) console.log(`Creator rewards: ${creatorRewards.toString()} wei`)
typescript
// Get first minter rewards const firstMinterRewards = await rewardTracker.getRecipientRewards( escrowAddress, collectionAddress, "FIRST_MINTER", firstMinterAddress ) console.log(`First minter rewards: ${firstMinterRewards.toString()} wei`)
typescript
// Get game owner rewards const gameOwnerRewards = await rewardTracker.getRecipientRewards( escrowAddress, collectionAddress, "GAME_OWNER", gameOwnerAddress ) console.log(`Game owner rewards: ${gameOwnerRewards.toString()} wei`)

All Recipients for a Collection

typescript
// Get rewards for all recipient types const allRewards = await Promise.all([ rewardTracker.getRecipientRewards(escrowAddress, collectionAddress, "CREATOR", creatorAddress), rewardTracker.getRecipientRewards(escrowAddress, collectionAddress, "FIRST_MINTER", firstMinterAddress), rewardTracker.getRecipientRewards(escrowAddress, collectionAddress, "GAME_OWNER", gameOwnerAddress), rewardTracker.getRecipientRewards(escrowAddress, collectionAddress, "PLATFORM", platformAddress) ]) const [creatorRewards, firstMinterRewards, gameOwnerRewards, platformRewards] = allRewards console.log("Reward Distribution:", { creator: creatorRewards.toString(), firstMinter: firstMinterRewards.toString(), gameOwner: gameOwnerRewards.toString(), platform: platformRewards.toString(), total: (creatorRewards + firstMinterRewards + gameOwnerRewards + platformRewards).toString() })

Reward Events

Tracking Reward Distributions

typescript
import { getRewardDistributionEvents } from '@b3dotfun/basemint' // Get historical reward distribution events const fromBlock = await publicClient.getBlockNumber() - 1000n const toBlock = await publicClient.getBlockNumber() const rewardEvents = await getRewardDistributionEvents( publicClient, escrowAddress, fromBlock, toBlock ) console.log("Recent reward distributions:") rewardEvents.forEach(event => { console.log({ collection: event.args.collection, recipient: event.args.recipient, recipientType: event.args.recipientType, amount: event.args.amount?.toString(), blockNumber: event.blockNumber }) })

Real-time Reward Monitoring

typescript
// Watch for new reward distributions const unwatch = publicClient.watchContractEvent({ address: escrowAddress, abi: rewardTracker.escrowAbi, eventName: 'RewardDistributed', onLogs: (logs) => { logs.forEach(log => { console.log('New reward distributed:', { collection: log.args.collection, recipient: log.args.recipient, amount: log.args.amount?.toString(), type: log.args.recipientType }) }) } }) // Stop watching when done // unwatch()

Reward Withdrawal

Individual Withdrawals

typescript
import { createWalletClient } from 'viem' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) const walletClient = createWalletClient({ chain: b3Testnet, transport: http(), account }) // Withdraw rewards for a specific collection and recipient type async function withdrawRewards( collectionAddress: string, recipientType: "CREATOR" | "FIRST_MINTER" | "GAME_OWNER" | "PLATFORM" ) { try { // Check available rewards first const availableRewards = await rewardTracker.getRecipientRewards( escrowAddress, collectionAddress, recipientType, account.address ) if (availableRewards === 0n) { console.log("No rewards available to withdraw") return } console.log(`Withdrawing ${availableRewards.toString()} wei of rewards...`) // Withdraw rewards const tx = await rewardTracker.withdrawRewards( walletClient, escrowAddress, collectionAddress, recipientType ) console.log(`✅ Rewards withdrawn successfully: ${tx}`) return tx } catch (error) { console.error("❌ Withdrawal failed:", error) throw error } } // Example: Creator withdraws their rewards await withdrawRewards(collectionAddress, "CREATOR")

Batch Withdrawals

typescript
// Withdraw rewards from multiple collections async function withdrawAllCreatorRewards(creatorAddress: string) { // Get all collections where this address is the creator const creatorCollections = await getCreatorCollections(creatorAddress) for (const collection of creatorCollections) { const rewards = await rewardTracker.getRecipientRewards( escrowAddress, collection.address, "CREATOR", creatorAddress ) if (rewards > 0n) { console.log(`Withdrawing ${rewards.toString()} wei from ${collection.name}`) try { await rewardTracker.withdrawRewards( walletClient, escrowAddress, collection.address, "CREATOR" ) console.log(`✅ Withdrawn from ${collection.name}`) } catch (error) { console.error(`❌ Failed to withdraw from ${collection.name}:`, error) } } } }

Reward Analytics

Portfolio Overview

typescript
async function getCreatorPortfolio(creatorAddress: string) { const collections = await getCreatorCollections(creatorAddress) let totalRewards = 0n let totalUnclaimedRewards = 0n let totalMints = 0n const collectionData = [] for (const collection of collections) { const collectionRewards = await rewardTracker.getCollectionRewards( escrowAddress, collection.address ) const creatorRewards = await rewardTracker.getRecipientRewards( escrowAddress, collection.address, "CREATOR", creatorAddress ) totalRewards += collectionRewards.totalRewards totalUnclaimedRewards += creatorRewards totalMints += collectionRewards.totalMints collectionData.push({ name: collection.name, address: collection.address, totalRewards: collectionRewards.totalRewards, creatorRewards, mints: collectionRewards.totalMints }) } return { summary: { totalCollections: collections.length, totalRewards: totalRewards.toString(), totalUnclaimedRewards: totalUnclaimedRewards.toString(), totalMints: totalMints.toString(), averageRewardPerMint: totalMints > 0n ? (totalRewards / totalMints).toString() : "0" }, collections: collectionData } } const portfolio = await getCreatorPortfolio(creatorAddress) console.log("Creator Portfolio:", portfolio)

Performance Metrics

typescript
async function getRewardMetrics(collectionAddress: string) { const collectionRewards = await rewardTracker.getCollectionRewards( escrowAddress, collectionAddress ) const collection = collectionManager.createCollection(collectionAddress, "ERC721") const currentSupply = await collection.totalSupply() const mintPrice = await collection.mintPrice() const totalMintRevenue = currentSupply * mintPrice const rewardPercentage = totalMintRevenue > 0n ? (collectionRewards.totalRewards * 100n) / totalMintRevenue : 0n return { totalMints: collectionRewards.totalMints.toString(), totalRewards: collectionRewards.totalRewards.toString(), totalRevenue: totalMintRevenue.toString(), rewardPercentage: rewardPercentage.toString() + "%", averageRewardPerMint: collectionRewards.totalMints > 0n ? (collectionRewards.totalRewards / collectionRewards.totalMints).toString() : "0" } }

Advanced Reward Features

Custom Reward Rates

Warning

Reward rate configuration is typically restricted to protocol administrators.

typescript
// For protocol administrators only async function updateRewardRates( adminWalletClient: any, escrowAddress: string ) { // Update reward rates (only callable by owner) await rewardTracker.setRewardRates( adminWalletClient, escrowAddress, { creatorRate: 4500n, // 45% firstMinterRate: 2500n, // 25% gameOwnerRate: 2000n, // 20% platformRate: 1000n // 10% } ) } // Check current reward rates const rates = await rewardTracker.getRewardRates(escrowAddress) console.log("Current reward rates:", { creator: rates.creatorRate.toString(), firstMinter: rates.firstMinterRate.toString(), gameOwner: rates.gameOwnerRate.toString(), platform: rates.platformRate.toString() })

Recipient Management

typescript
// Check if a recipient type is active const isCreatorActive = await rewardTracker.isRecipientActive( escrowAddress, "CREATOR" ) // Get recipient information const recipientInfo = await rewardTracker.getRecipientInfo( escrowAddress, "CREATOR" ) console.log("Creator recipient info:", { rate: recipientInfo.rate.toString(), isActive: recipientInfo.isActive })

Integration Best Practices

User Interface Integration

typescript
// Format rewards for display function formatRewards(weiAmount: bigint): string { const eth = Number(weiAmount) / 1e18 return `${eth.toFixed(6)} ETH` } // Calculate USD value (example with price feed) async function getRewardValueUSD(weiAmount: bigint, ethPriceUSD: number): Promise<string> { const eth = Number(weiAmount) / 1e18 const usd = eth * ethPriceUSD return `$${usd.toFixed(2)}` } // Check if withdrawal is profitable (considering gas costs) async function isWithdrawalProfitable( rewardAmount: bigint, gasPrice: bigint, gasLimit: bigint ): Promise<boolean> { const gasCost = gasPrice * gasLimit return rewardAmount > gasCost * 2n // Require 2x gas cost minimum }

Automated Reward Claiming

typescript
// Automated reward claiming service class RewardClaimingService { private rewardTracker: RewardTracker private walletClient: any private minWithdrawThreshold: bigint constructor(rewardTracker: RewardTracker, walletClient: any, minThreshold: bigint) { this.rewardTracker = rewardTracker this.walletClient = walletClient this.minWithdrawThreshold = minThreshold } async checkAndClaimRewards( collections: string[], recipientType: "CREATOR" | "FIRST_MINTER" | "GAME_OWNER" ) { for (const collectionAddress of collections) { try { const rewards = await this.rewardTracker.getRecipientRewards( escrowAddress, collectionAddress, recipientType, this.walletClient.account.address ) if (rewards >= this.minWithdrawThreshold) { console.log(`Claiming ${rewards.toString()} wei from ${collectionAddress}`) await this.rewardTracker.withdrawRewards( this.walletClient, escrowAddress, collectionAddress, recipientType ) console.log(`✅ Successfully claimed rewards from ${collectionAddress}`) } } catch (error) { console.error(`❌ Failed to claim from ${collectionAddress}:`, error) } } } } // Usage const claimingService = new RewardClaimingService( rewardTracker, walletClient, parseEther("0.001") // Minimum 0.001 ETH to claim ) await claimingService.checkAndClaimRewards( creatorCollections, "CREATOR" )

Troubleshooting

  • Verify the collection has received mints after deployment
  • Check that you're querying the correct recipient type and address
  • Ensure the collection was created with proper signatures
  • Confirm you have available rewards to withdraw
  • Check that you have sufficient gas for the transaction
  • Verify you're using the correct recipient type for your role
  • Check the current reward rates configuration
  • Verify the mint price and volume calculations
  • Ensure you're using the correct escrow contract address

Next Steps

Storage Service

Learn how to manage collection metadata with BaseMint storage

Learn More
Examples

See complete implementation examples including reward tracking

Learn More
Ask a question... ⌘I