Build on vibe.market’s smart contracts to create your own integrations, trading bots, or applications for digital collectibles.

Smart Contract Addresses

All vibe.market contracts are deployed on Base. Each booster pack collection has its own contract addresses for the NFT (BoosterDropV2) and token (BoosterTokenV2). Source availability: To discourage economic exploits on our unboxing system with immutable contracts, we’ve opted to not currently share the algorithms we use with Pyth Network for generating rarities. While we do provide multiple inputs to prevent randomness exploits, we will evaluate at a later date if our algorithms are safe to share. In the meantime our interfaces are public for developers, and we’ve performed security scanning via Almanax.

Contract Interfaces

View the complete smart contract interfaces

Common Use Cases

1. Buying Booster Packs

Purchase digital collectible booster packs programmatically and earn referral fees.
// Using ethers.js v6
import { ethers } from "ethers";

// Connect to provider
const provider = new ethers.JsonRpcProvider("https://base.llamarpc.com");
const signer = new ethers.Wallet(PRIVATE_KEY, provider);

// BoosterDropV2 contract
const boosterDrop = new ethers.Contract(
  BOOSTER_DROP_ADDRESS,
  IBoosterDropV2_ABI,
  signer
);

// Get mint price for packs (example: 5 packs)
const mintPrice = await boosterDrop.getMintPrice(5);

// Mint packs with referral (earn fees!)
await boosterDrop.mint(
  5, // amount of packs
  signer.address, // recipient
  YOUR_ADDRESS, // referrer (example: earn portion of fees)
  YOUR_ADDRESS, // originReferrer (example: earn additional portion)
  { value: mintPrice } // ETH payment
);

2. Selling Tickets (Tokens)

Trade collectible tokens through the bonding curve or Uniswap pool.
// BoosterTokenV2 contract
const boosterToken = new ethers.Contract(
  BOOSTER_TOKEN_ADDRESS,
  IBoosterTokenV2_ABI,
  signer
);

// Check current market type
const marketType = await boosterToken.marketType();
// 0 = BONDING_CURVE, 1 = UNISWAP_POOL

// Get sell quote for tokens (example: 100,000 tokens)
const tokenAmount = ethers.parseUnits("100000", 18);
const ethReceived = await boosterToken.getTokenSellQuote(tokenAmount);

// Sell tokens with slippage protection
const minPayout = (ethReceived * 98n) / 100n; // Example: 2% slippage
await boosterToken.sell(
  tokenAmount,
  signer.address, // recipient of ETH
  minPayout, // minimum ETH to accept
  YOUR_ADDRESS, // referrer
  YOUR_ADDRESS // originReferrer
);

3. Opening Packs and Getting Randomness

Open packs to reveal rarity using Pyth Network entropy.
// Get entropy fee required for opening
const entropyFee = await boosterDrop.getEntropyFee();

// Open multiple packs (requires ETH for entropy)
const tokenIds = [1, 2, 3]; // Your unopened pack token IDs
await boosterDrop.open(tokenIds, { value: entropyFee });

// Get rarity info after randomness is fulfilled
const rarityInfo = await boosterDrop.getTokenRarity(tokenIds[0]);
console.log({
  rarity: rarityInfo.rarity, // 1=Common, 2=Rare, 3=Epic, 4=Legendary
  randomValue: rarityInfo.randomValue,
  randomness: rarityInfo.tokenSpecificRandomness,
});

4. Selling NFTs for Token Offers

Sell both opened and unopened packs back to the contract.
// Sell single NFT (opened or unopened)
await boosterDrop.sellAndClaimOffer(tokenId);

// Batch sell multiple NFTs
const tokenIds = [1, 2, 3];
await boosterDrop.sellAndClaimOfferBatch(tokenIds);

// Exchange values are examples only - actual values vary by collection:
// Unopened packs example: 80,000-100,000 tokens (80-100% of mint cost) [100% for packs created after August 1st, 2025]
// Opened packs examples based on rarity:
// - Common: 20,000 tokens (example)
// - Rare: 120,000 tokens (example)
// - Epic: 420,000 tokens (example)
// - Legendary: 3,000,000 tokens (example)
// Note: This optional exchange feature may not be available for all collections

5. Reading Onchain Data

Access all card data directly from the blockchain.
// Check if pack is opened by getting rarity
try {
  const rarityInfo = await boosterDrop.getTokenRarity(tokenId);
  console.log("Pack is opened, rarity:", rarityInfo.rarity);
} catch {
  console.log("Pack is unopened");
}

// Track mint events
const mintFilter = boosterDrop.filters.BoosterDropsMinted();
const mintEvents = await boosterDrop.queryFilter(mintFilter);

// Track pack openings
const openFilter = boosterDrop.filters.BoosterDropOpened();
const openEvents = await boosterDrop.queryFilter(openFilter);

// Get token metadata URI
const tokenURI = await boosterDrop.tokenURI(tokenId);

6. Building Games with vibe.market Cards

// Track user's cards via transfer events
const userAddress = "0x...";
const transferFilter = boosterDrop.filters.BoosterDropTransfer(
  null,
  userAddress
);
const transferEvents = await boosterDrop.queryFilter(transferFilter);

// Get card details including randomness for game mechanics
const userCards = [];
for (const event of transferEvents) {
  const tokenId = event.args.tokenId;

  try {
    const rarityInfo = await boosterDrop.getTokenRarity(tokenId);
    userCards.push({
      tokenId,
      rarity: rarityInfo.rarity,
      randomValue: rarityInfo.randomValue, // Use for game RNG
      randomness: rarityInfo.tokenSpecificRandomness, // Unique per card
      isOpened: true,
    });

    // Use randomness for unique card attributes (example values)
    const uniqueSeed = rarityInfo.randomValue;
    const attackPower = 100 + (uniqueSeed % 50); // Example calculation
    const defense = 80 + (uniqueSeed % 30); // Example calculation
  } catch {
    // Rarity not defined - pack hasn't been opened yet
    userCards.push({
      tokenId,
      rarity: 0,
      isOpened: false,
    });
  }
}

// Grant game benefits based on collection
const legendaryCount = userCards.filter((c) => c.rarity === 4).length;
if (legendaryCount > 0) {
  // Unlock special game features
}

7. Onchain Wear & Foil

We deployed the following source-available contract at 0x002aaaa42354bf8f09f9924977bf0c531933f999 that allows you to query wear and foil onchain from getTokenRarity using tokenSpecificRandomness. 0.5% of cards are standard foil, and 0.05% of cards are prize foil. Wear is distributed between 0 and 1.
// IBoosterCardSeedUtils contract address on Base
const SEED_UTILS_ADDRESS = "0x002aaaa42354bf8f09f9924977bf0c531933f999";

// Connect to the BoosterCardSeedUtils contract
const seedUtils = new ethers.Contract(
  SEED_UTILS_ADDRESS,
  IBoosterCardSeedUtils_ABI,
  provider
);

// Get rarity info from a card (must be opened)
const tokenId = 123; // Your opened pack token ID
const rarityInfo = await boosterDrop.getTokenRarity(tokenId);

// Use tokenSpecificRandomness as the seed for wear & foil
const seed = rarityInfo.tokenSpecificRandomness;

// Get wear value (string with 10 decimal places, e.g., "0.1234567890")
const wear = await seedUtils.wearFromSeed(seed);
console.log("Card wear:", wear);

// Get foil type (returns "Prize", "Standard", or "Normal")
const foilType = await seedUtils.getFoilMappingFromSeed(seed);
console.log("Card foil:", foilType);

// Get both wear and foil in a single call
const cardData = await seedUtils.getCardSeedData(seed);
console.log({
  wear: cardData.wear, // e.g., "0.0123456789"
  foilType: cardData.foilType, // e.g., "Normal", "Standard", or "Prize"
});

// Example: Process multiple cards to get their wear & foil
const userCards = [101, 102, 103]; // Token IDs of opened packs
const cardDetails = [];

for (const tokenId of userCards) {
  try {
    const rarityInfo = await boosterDrop.getTokenRarity(tokenId);
    const [wear, foilType] = await seedUtils.getCardSeedData(
      rarityInfo.tokenSpecificRandomness
    );

    cardDetails.push({
      tokenId,
      rarity: rarityInfo.rarity,
      wear,
      foilType,
      // Use wear for game mechanics
      condition:
        parseFloat(wear) < 0.05
          ? "Pristine"
          : parseFloat(wear) < 0.2
          ? "Mint"
          : parseFloat(wear) < 0.45
          ? "Lightly Played"
          : parseFloat(wear) < 0.75
          ? "Moderately Played"
          : "Heavily Played",
    });
  } catch (error) {
    console.log(`Token ${tokenId} is unopened or invalid`);
  }
}

// Build game features based on foil & wear
const prizeCards = cardDetails.filter((c) => c.foilType === "Prize");
const mintConditionCards = cardDetails.filter((c) => parseFloat(c.wear) < 0.05);

8. Card Rarity Distribution

Cards in a pack are mapped to specific random value ranges based on their randomValue. When a pack is opened, the on-chain randomness generates a value between 0-999,999 returned in randomValue from getTokenRarity(). Example Random Value Mapping:
Random Value Range (0 - 999,999)
├─ Legendary (0 - 4,999): 0.5%
│  └─ Cards split range evenly (e.g., 10 cards = ~500 values each)
├─ Epic (5,000 - 84,999): 8.0%
│  └─ Cards split range evenly (e.g., 20 cards = ~4,000 values each)
├─ Rare (85,000 - 334,999): 25.0%
│  └─ Cards split range evenly (e.g., 30 cards = ~8,333 values each)
└─ Common (335,000 - 999,999): 66.5%
   └─ Cards split range evenly (e.g., 40 cards = ~16,625 values each)

Important Considerations

Note: All numerical values in code examples are for illustration purposes only. Actual values, fees, and percentages are determined by a collection’s smart contracts and may vary significantly. These digital collectibles are intended solely for personal enjoyment and entertainment.
  • Gas Optimization: Batch operations when possible (e.g., opening multiple packs at once)
  • Slippage Protection: Always use minPayoutSize when selling tokens
  • Referral System: Include referral addresses to earn trading fees (example: 1% of trades, which might be 20% of a 7.5% total fee)
  • Market State: Check if market is on bonding curve or Uniswap before trading
  • Entropy Fees: Opening packs requires a small ETH fee for Pyth Network randomness (example: ~$0.01)
  • Token Offers: The optional NFT exchange feature mints new tokens when available - this feature is not guaranteed and varies by collection

Next Steps