Quoting, monitoring, and metrics

Examples for getting swap quotes, monitoring auction progress, and fetching token details

Price quotes

/**
 * Example: Price Quoter
 * 
 * This example demonstrates:
 * - Getting price quotes across Uniswap V2, V3, and V4
 * - Comparing quotes to find best prices
 * - Handling different swap types (exact input/output)
 */

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

import { createPublicClient, http, parseEther, formatEther, type Address } from 'viem'
import { base } from 'viem/chains'

const token = process.env.TOKEN as `0x${string}`;
const rpcUrl = process.env.RPC_URL || 'https://mainnet.base.org' as string;

if (!token) throw new Error('TOKEN is not set');

// Example token addresses (replace with actual addresses)
const weth = '0x4200000000000000000000000000000000000006' as Address // WETH on Base
const usdc = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address // USDC on Base

async function main() {
  // Initialize SDK
  const publicClient = createPublicClient({
    chain: base,
    transport: http(rpcUrl)
  })

  const sdk = new DopplerSDK({
    publicClient,
    chainId: base.id
  })

  const quoter = sdk.quoter

  console.log('💱 Price Quoter Example')
  console.log('=====================')

  // Example 1: Quote exact input on V3
  console.log('\n📊 Example 1: Swap 1 ETH for USDC on V3')
  try {
    const v3Quote = await quoter.quoteExactInputV3({
      tokenIn: weth,
      tokenOut: usdc,
      amountIn: parseEther('1'),
      fee: 3000 // 0.3% fee tier
    })
    
    console.log('- Amount out:', formatEther(v3Quote.amountOut), 'USDC')
    console.log('- Price impact (ticks crossed):', v3Quote.initializedTicksCrossed)
    console.log('- Gas estimate:', v3Quote.gasEstimate.toString())
    console.log('- Final sqrtPriceX96:', v3Quote.sqrtPriceX96After.toString())
  } catch (error) {
    console.error('V3 quote failed:', error.message)
  }

  // Example 2: Quote exact output on V3
  console.log('\n📊 Example 2: Get exactly 2000 USDC, pay in ETH on V3')
  try {
    const v3QuoteOut = await quoter.quoteExactOutputV3({
      tokenIn: weth,
      tokenOut: usdc,
      amountOut: parseEther('2000'), // Want exactly 2000 USDC
      fee: 3000
    })
    
    console.log('- Amount in required:', formatEther(v3QuoteOut.amountIn), 'ETH')
    console.log('- Price impact (ticks crossed):', v3QuoteOut.initializedTicksCrossed)
    console.log('- Gas estimate:', v3QuoteOut.gasEstimate.toString())
  } catch (error) {
    console.error('V3 exact output quote failed:', error.message)
  }

  // Example 3: Quote on V2 (if available)
  console.log('\n📊 Example 3: Swap 1 ETH for USDC on V2')
  try {
    const v2Quote = await quoter.quoteExactInputV2({
      amountIn: parseEther('1'),
      path: [weth, usdc]
    })
    
    console.log('- Amount out:', formatEther(v2Quote[1]), 'USDC')
    console.log('- Simple constant product AMM pricing')
  } catch (error) {
    console.error('V2 quote failed:', error.message)
  }

  // Example 4: Multi-hop V2 quote
  console.log('\n📊 Example 4: Multi-hop swap ETH -> USDC -> TOKEN on V2')
  try {
    const multiHopQuote = await quoter.quoteExactInputV2({
      amountIn: parseEther('1'),
      path: [weth, usdc, token] // ETH -> USDC -> TOKEN
    })
    
    console.log('Hop results:')
    console.log('- Start:', formatEther(multiHopQuote[0]), 'ETH')
    console.log('- After hop 1:', formatEther(multiHopQuote[1]), 'USDC')
    console.log('- Final:', formatEther(multiHopQuote[2]), 'TOKEN')
  } catch (error) {
    console.error('Multi-hop quote failed:', error.message)
  }

  // Example 5: V4 quote (for graduated dynamic auctions)
  console.log('\n📊 Example 5: Swap on V4 pool')
  try {
    const v4PoolKey = {
      currency0: weth,
      currency1: token,
      fee: 3000,
      tickSpacing: 60,
      hooks: '0x0000000000000000000000000000000000000000' as Address // No hook for graduated pool
    }
    
    const v4Quote = await quoter.quoteExactInputV4({
      poolKey: v4PoolKey,
      zeroForOne: true, // Swapping currency0 (WETH) for currency1 (TOKEN)
      exactAmount: parseEther('1')
    })
    
    console.log('- Amount out:', formatEther(v4Quote.amountOut), 'TOKEN')
    console.log('- Gas estimate:', v4Quote.gasEstimate.toString())
  } catch (error) {
    console.error('V4 quote failed:', error.message)
  }

  // Example 6: Compare quotes across versions
  console.log('\n🔄 Comparing quotes for 1 ETH -> USDC:')
  const results: { version: string; amountOut: bigint; gas: bigint }[] = []
  
  // Try V2
  try {
    const v2 = await quoter.quoteExactInputV2({
      amountIn: parseEther('1'),
      path: [weth, usdc]
    })
    results.push({ 
      version: 'V2', 
      amountOut: v2[1], 
      gas: BigInt(100000) // Approximate
    })
  } catch {}
  
  // Try V3
  try {
    const v3 = await quoter.quoteExactInputV3({
      tokenIn: weth,
      tokenOut: usdc,
      amountIn: parseEther('1'),
      fee: 3000
    })
    results.push({ 
      version: 'V3', 
      amountOut: v3.amountOut, 
      gas: v3.gasEstimate 
    })
  } catch {}
  
  // Sort by best output
  results.sort((a, b) => Number(b.amountOut - a.amountOut))
  
  console.log('\nBest quotes (sorted by output):')
  results.forEach((result, i) => {
    console.log(`${i + 1}. ${result.version}: ${formatEther(result.amountOut)} USDC (gas: ${result.gas})`)
  })
  
  if (results.length > 0) {
    console.log(`\n✅ Best option: ${results[0].version} with ${formatEther(results[0].amountOut)} USDC`)
  }

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

main()

Auction monitoring

/**
 * Example: Monitor Existing Auctions
 *
 * This example demonstrates:
 * - Monitoring static and dynamic auctions
 * - Checking graduation status
 * - Tracking key metrics and progress
 */

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

import { DopplerSDK } from '../src';
import {
  http,
  formatEther,
  type Address,
  createPublicClient,
} from 'viem';
import { base } from 'viem/chains';

// Example addresses - replace with your actual auction addresses
const staticPoolAddress = process.env.STATIC_POOL_ADDRESS as `0x${string}`;
const dynamicHookAddress = process.env.DYNAMIC_HOOK_ADDRESS as `0x${string}`;
const rpcUrl = process.env.RPC_URL || ('https://mainnet.base.org' as string);

async function monitorStaticAuction(sdk: DopplerSDK, poolAddress: Address) {
  console.log('\n📊 Monitoring Static Auction...');
  console.log('Pool address:', poolAddress);

  try {
    const auction = await sdk.getStaticAuction(poolAddress);

    // Get pool information
    const poolInfo = await auction.getPoolInfo();
    console.log('\nPool Information:');
    console.log('- Token:', poolInfo.tokenAddress);
    console.log('- Numeraire:', poolInfo.numeraireAddress);
    console.log('- Fee tier:', poolInfo.fee / 10000, '%');
    console.log('- Liquidity:', formatEther(poolInfo.liquidity));
    console.log('- SqrtPriceX96:', poolInfo.sqrtPriceX96.toString());

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

    // Check graduation status
    const hasGraduated = await auction.hasGraduated();
    console.log(
      '\nGraduation status:',
      hasGraduated ? '✅ Graduated' : '⏳ Active'
    );

    // Get token address for further info
    const tokenAddress = await auction.getTokenAddress();
    console.log('Token contract:', tokenAddress);
  } catch (error) {
    console.error('Error monitoring static auction:', error);
  }
}

async function monitorDynamicAuction(sdk: DopplerSDK, hookAddress: Address) {
  console.log('\n📊 Monitoring Dynamic Auction...');
  console.log('Hook address:', hookAddress);

  try {
    const auction = await sdk.getDynamicAuction(hookAddress);

    // Get comprehensive hook information
    const hookInfo = await auction.getHookInfo();
    console.log('\nHook Information:');
    console.log('- Token:', hookInfo.tokenAddress);
    console.log('- Numeraire:', hookInfo.numeraireAddress);
    console.log('- Pool ID:', hookInfo.poolId);
    console.log('- Current epoch:', hookInfo.currentEpoch);

    console.log('\nSale Progress:');
    console.log(
      '- Total proceeds:',
      formatEther(hookInfo.totalProceeds),
      'ETH'
    );
    console.log('- Tokens sold:', formatEther(hookInfo.totalTokensSold));
    console.log(
      '- Min proceeds:',
      formatEther(hookInfo.minimumProceeds),
      'ETH'
    );
    console.log(
      '- Max proceeds:',
      formatEther(hookInfo.maximumProceeds),
      'ETH'
    );

    console.log('\nAuction Status:');
    console.log('- Early exit:', hookInfo.earlyExit ? '✅ Yes' : '❌ No');
    console.log(
      '- Insufficient proceeds:',
      hookInfo.insufficientProceeds ? '⚠️ Yes' : '✅ No'
    );

    // Calculate time remaining
    const now = BigInt(Math.floor(Date.now() / 1000));
    if (now < hookInfo.endingTime) {
      const remaining = Number(hookInfo.endingTime - now);
      const hours = Math.floor(remaining / 3600);
      const minutes = Math.floor((remaining % 3600) / 60);
      console.log('- Time remaining:', `${hours}h ${minutes}m`);
    } else {
      console.log('- Time remaining: Auction ended');
    }

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

    // Check if ended early
    const hasEndedEarly = await auction.hasEndedEarly();
    if (hasEndedEarly) {
      console.log('\n🎯 Auction ended early due to reaching max proceeds');
    }

    // Check graduation
    const hasGraduated = await auction.hasGraduated();
    console.log(
      'Graduation status:',
      hasGraduated ? '✅ Graduated' : '⏳ Active'
    );
  } catch (error) {
    console.error('Error monitoring dynamic auction:', error);
  }
}

async function main() {
  // Initialize SDK in read-only mode
  const publicClient = createPublicClient({
    chain: base,
    transport: http(rpcUrl),
  });

  const sdk = new DopplerSDK({
    publicClient,
    chainId: base.id,
  });

  console.log('🔍 Doppler Auction Monitor');
  console.log('=======================');

  // Monitor static auction if address provided
  if (staticPoolAddress) {
    await monitorStaticAuction(sdk, staticPoolAddress);
  } else {
    console.log('\n⚠️  No static auction address provided');
  }

  // Monitor dynamic auction if address provided
  if (dynamicHookAddress) {
    await monitorDynamicAuction(sdk, dynamicHookAddress);
  } else {
    console.log('\n⚠️  No dynamic auction address provided');
  }

  console.log('\n✨ Monitoring complete!');
}

main()

Token interactions

/**
 * Example: Token Interaction
 *
 * This example demonstrates:
 * - Interacting with DERC20 tokens launched via Doppler
 * - Checking balances and vesting data
 * - Approving spending and releasing vested tokens
 */

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

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

// Configuration
const spender = process.env.SPENDER as `0x${string}`;
const privateKey = process.env.PRIVATE_KEY as `0x${string}`;
const rpcUrl = process.env.RPC_URL || 'https://mainnet.base.org' as string;
const tokenAddress = process.env.TOKEN_ADDRESS as `0x${string}`;

if (!privateKey) throw new Error('PRIVATE_KEY is not set');
if (!tokenAddress) throw new Error('TOKEN_ADDRESS is not set');
if (!spender) throw new Error('SPENDER 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,
  });

  console.log('💰 Token Interaction Example');
  console.log('===========================');
  console.log('Account:', account.address);
  console.log('Token:', tokenAddress);

  // 2. Create token instance
  const token = new Derc20(publicClient, walletClient, tokenAddress);

  try {
    // 3. Get token information
    console.log('\n📋 Token Information:');
    const [name, symbol, decimals, totalSupply] = await Promise.all([
      token.getName(),
      token.getSymbol(),
      token.getDecimals(),
      token.getTotalSupply(),
    ]);

    console.log('- Name:', name);
    console.log('- Symbol:', symbol);
    console.log('- Decimals:', decimals);
    console.log('- Total Supply:', formatEther(totalSupply), symbol);

    // 4. Check balances
    console.log('\n💸 Balances:');
    const balance = await token.getBalanceOf(account.address);
    console.log('- Your balance:', formatEther(balance), symbol);

    // Also check ETH balance
    const eth = new Eth(publicClient);
    const ethBalance = await eth.getBalanceOf(account.address);
    console.log('- ETH balance:', formatEther(ethBalance), 'ETH');

    // 5. Check vesting information
    console.log('\n⏰ Vesting Information:');
    const [vestingDuration, vestingStart, vestedTotal] = await Promise.all([
      token.getVestingDuration(),
      token.getVestingStart(),
      token.getVestedTotalAmount(),
    ]);

    if (vestingDuration > 0n) {
      const vestingEndTime = vestingStart + vestingDuration;
      const now = BigInt(Math.floor(Date.now() / 1000));
      const isVestingActive = now < vestingEndTime;

      console.log(
        '- Vesting duration:',
        Number(vestingDuration) / 86400,
        'days'
      );
      console.log(
        '- Vesting start:',
        new Date(Number(vestingStart) * 1000).toLocaleString()
      );
      console.log('- Total vested amount:', formatEther(vestedTotal), symbol);
      console.log('- Vesting active:', isVestingActive);

      // Check user's vesting data
      const vestingData = await token.getVestingData(account.address);
      console.log('\n📊 Your Vesting Data:');
      console.log(
        '- Total vested:',
        formatEther(vestingData.totalAmount),
        symbol
      );
      console.log(
        '- Already released:',
        formatEther(vestingData.releasedAmount),
        symbol
      );

      // Calculate available to release
      const available = await token.getAvailableVestedAmount(account.address);
      console.log('- Available to release:', formatEther(available), symbol);

      // Release vested tokens if available
      if (available > 0n) {
        console.log('\n🎯 Releasing vested tokens...');
        try {
          const txHash = await token.release(available);
          console.log('✅ Tokens released! Transaction:', txHash);
        } catch (error) {
          console.error('❌ Failed to release tokens:', error);
        }
      }
    } else {
      console.log('- No vesting configured for this token');
    }

    // 6. Token approvals
    console.log('\n🔓 Token Approvals:');
    const currentAllowance = await token.getAllowance(account.address, spender);
    console.log('- Current allowance:', formatEther(currentAllowance), symbol);

    // Approve spending if needed
    const approvalAmount = parseEther('100');
    if (currentAllowance < approvalAmount) {
      console.log(
        `\n📝 Approving ${formatEther(approvalAmount)} ${symbol} for spender...`
      );
      try {
        const txHash = await token.approve(spender, approvalAmount);
        console.log('✅ Approval successful! Transaction:', txHash);
      } catch (error) {
        console.error('❌ Approval failed:', error);
      }
    }

    // 7. Additional token info
    console.log('\n🔍 Additional Information:');
    const [tokenURI, pool, isPoolUnlocked, yearlyMintRate] = await Promise.all([
      token.getTokenURI(),
      token.getPool(),
      token.getIsPoolUnlocked(),
      token.getYearlyMintRate(),
    ]);

    console.log('- Token URI:', tokenURI);
    console.log('- Pool address:', pool);
    console.log('- Pool unlocked:', isPoolUnlocked);
    console.log(
      '- Yearly mint rate:',
      formatEther(yearlyMintRate),
      symbol,
      'per year'
    );
  } catch (error) {
    console.error('\n❌ Error:', error);
    process.exit(1);
  }

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

main();

Last updated