Dynamic auctions

Create coins with Doppler's dynamic bonding curve, aka Doppler v4

/**
 * Example: Create a Dynamic Auction with Uniswap V4 Migration
 *
 * This example demonstrates:
 * - Creating a gradual Dutch auction using Uniswap V4 hooks
 * - Configuring dynamic price movement with epochs
 * - Setting up V4 migration with fee streaming
 */

// UNCOMMENT IF RUNNING LOCALLY
// import { DopplerSDK, DynamicAuctionBuilder } from '@whetstone-research/doppler-sdk';

import { DopplerSDK } from '../src';

import {
  createPublicClient,
  createWalletClient,
  http,
  parseEther,
  formatEther,
} from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

// Load environment variables
const privateKey = process.env.PRIVATE_KEY as `0x${string}`;
const rpcUrl = process.env.RPC_URL || 'https://mainnet.base.org' as string;

if (!privateKey) throw new Error('PRIVATE_KEY is not set');

async function main() {
  // 1. Set up clients
  const account = privateKeyToAccount(privateKey);

  const publicClient = createPublicClient({
    chain: base,
    transport: http(rpcUrl),
  });

  const walletClient = createWalletClient({
    chain: base,
    transport: http(rpcUrl),
    account,
  });

  // 2. Initialize SDK
  const sdk = new DopplerSDK({
    publicClient,
    walletClient,
    chainId: base.id,
  });

  // 3. Define dynamic auction parameters via builder
  const params = sdk.buildDynamicAuction()
    .tokenConfig({
      name: 'TEST DYNAMIC',
      symbol: 'TEST',
      tokenURI: 'https://example.com/dynamic-token.json',
    })
    .saleConfig({
      initialSupply: parseEther('10000000'), // 10M tokens
      numTokensToSell: parseEther('5000000'), // Sell 5M tokens
      numeraire: '0x4200000000000000000000000000000000000006', // WETH on Base
    })
    .poolConfig({ fee: 3000, tickSpacing: 60 })
    .auctionByTicks({
      durationDays: 7,
      epochLength: 3600,
      startTick: -92103,
      endTick: -69080,
      minProceeds: parseEther('100'),
      maxProceeds: parseEther('5000'),
    })
    .withMigration({
      type: 'uniswapV4',
      fee: 3000,
      tickSpacing: 60,
      streamableFees: {
        lockDuration: 365 * 24 * 60 * 60,
        beneficiaries: [
          { address: account.address, percentage: 10000 }, // 100%
          // Modify beneficiaries as needed
          // { address: '0xBeneficiary1...', percentage: 5000 }, // 50%
          // { address: '0xBeneficiary2...', percentage: 3000 }, // 30%
          // { address: '0xBeneficiary3...', percentage: 2000 }, // 20%
        ],
      },
    })
    .withGovernance({ type: 'default' })
    .withUserAddress(account.address)
    .build();

  console.log('Creating dynamic auction...');
  console.log('Token:', params.token.name, `(${params.token.symbol})`);
  console.log('Selling:', formatEther(params.sale.numTokensToSell), 'tokens');
  console.log('Duration:', params.auction.duration, 'days');
  console.log('Epochs:', params.auction.duration * 24, 'total (1 hour each)');
  console.log('Price will gradually increase from ~0.0001 to ~0.001 ETH');

  try {
    // 4. Create the dynamic auction
    const result = await sdk.factory.createDynamicAuction(params);

    console.log('\n✅ Dynamic auction created successfully!');
    console.log('Hook address:', result.hookAddress);
    console.log('Token address:', result.tokenAddress);
    console.log('Pool ID:', result.poolId);
    console.log('Transaction:', result.transactionHash);

    // 5. Get the auction instance
    const auction = await sdk.getDynamicAuction(result.hookAddress);

    // 6. Monitor the auction
    console.log('\nMonitoring dynamic auction...');

    const hookInfo = await auction.getHookInfo();
    console.log('Current epoch:', hookInfo.currentEpoch);
    console.log('Total proceeds:', formatEther(hookInfo.totalProceeds), 'ETH');
    console.log('Tokens sold:', formatEther(hookInfo.totalTokensSold));

    // 7. Get current price (as tick)
    const currentTick = await auction.getCurrentPrice();
    console.log('\nCurrent tick:', currentTick.toString());

    // 8. Check if auction ended early
    const hasEndedEarly = await auction.hasEndedEarly();
    if (hasEndedEarly) {
      console.log('\n🎯 Auction ended early - reached max proceeds!');
    } else {
      console.log('\nAuction is active. It will end when:');
      console.log('- All epochs complete (7 days)');
      console.log('- OR max proceeds reached (5000 ETH)');
      console.log('- OR insufficient demand detected');
    }

    // 9. Show graduation criteria
    const hasGraduated = await auction.hasGraduated();
    console.log('\nHas graduated:', hasGraduated);

    if (!hasGraduated) {
      console.log('After auction ends with sufficient proceeds:');
      console.log('- Liquidity migrates to Uniswap V4');
      console.log('- LP fees stream to beneficiaries over 1 year');
      console.log('- Token becomes freely tradeable');
    }
  } catch (error) {
    console.error('\n❌ Error creating dynamic auction:', error);
    process.exit(1);
  }

  console.log('\n✨ Example completed!');
}

main();

Last updated