import { onchainTable, index } from 'ponder'; import { TAX_RATE_OPTIONS } from 'kraiken-lib/taxRates'; export const HOURS_IN_RING_BUFFER = 168; // 7 days * 24 hours const RING_BUFFER_SEGMENTS = 4; // ethReserve, minted, burned, holderCount export const stackMeta = onchainTable('stackMeta', t => ({ id: t.text().primaryKey(), contractVersion: t .integer() .notNull() .$default(() => 0), ponderVersion: t .text() .notNull() .$default(() => 'unknown'), kraikenLibVersion: t .integer() .notNull() .$default(() => 0), updatedAt: t .bigint() .notNull() .$default(() => 0n), })); // Global protocol stats - singleton with id "0x01" export const stats = onchainTable('stats', t => ({ id: t.text().primaryKey(), // Always "0x01" kraikenTotalSupply: t .bigint() .notNull() .$default(() => 0n), stakeTotalSupply: t .bigint() .notNull() .$default(() => 0n), outstandingStake: t .bigint() .notNull() .$default(() => 0n), positionsUpdatedAt: t .bigint() .notNull() .$default(() => 0n), minStake: t .bigint() .notNull() .$default(() => 0n), // Totals totalMinted: t .bigint() .notNull() .$default(() => 0n), totalBurned: t .bigint() .notNull() .$default(() => 0n), totalTaxPaid: t .bigint() .notNull() .$default(() => 0n), // Rolling windows - calculated from ring buffer mintedLastWeek: t .bigint() .notNull() .$default(() => 0n), mintedLastDay: t .bigint() .notNull() .$default(() => 0n), mintNextHourProjected: t .bigint() .notNull() .$default(() => 0n), burnedLastWeek: t .bigint() .notNull() .$default(() => 0n), burnedLastDay: t .bigint() .notNull() .$default(() => 0n), burnNextHourProjected: t .bigint() .notNull() .$default(() => 0n), taxPaidLastWeek: t .bigint() .notNull() .$default(() => 0n), taxPaidLastDay: t .bigint() .notNull() .$default(() => 0n), taxPaidNextHourProjected: t .bigint() .notNull() .$default(() => 0n), // Hourly ETH reserve snapshots (from ring buffer slot 0) ethReserveLastDay: t .bigint() .notNull() .$default(() => 0n), ethReserveLastWeek: t .bigint() .notNull() .$default(() => 0n), // Net supply change (minted - burned) netSupplyChangeDay: t .bigint() .notNull() .$default(() => 0n), netSupplyChangeWeek: t .bigint() .notNull() .$default(() => 0n), // Ring buffer state (flattened array of length HOURS_IN_RING_BUFFER * 4) ringBufferPointer: t .integer() .notNull() .$default(() => 0), lastHourlyUpdateTimestamp: t .bigint() .notNull() .$default(() => 0n), ringBuffer: t .jsonb() .$type() .notNull() .$default(() => Array(HOURS_IN_RING_BUFFER * RING_BUFFER_SEGMENTS).fill('0')), // LiquidityManager stats holderCount: t .integer() .notNull() .$default(() => 0), lastRecenterTimestamp: t .bigint() .notNull() .$default(() => 0n), lastRecenterTick: t .integer() .notNull() .$default(() => 0), recentersLastDay: t .integer() .notNull() .$default(() => 0), recentersLastWeek: t .integer() .notNull() .$default(() => 0), lastEthReserve: t .bigint() .notNull() .$default(() => 0n), lastVwapTick: t .integer() .notNull() .$default(() => 0), // 7-day ETH reserve growth metrics ethReserve7dAgo: t.bigint(), ethReserveGrowthBps: t.integer(), // 7-day trading fees earned feesEarned7dEth: t .bigint() .notNull() .$default(() => 0n), feesEarned7dKrk: t .bigint() .notNull() .$default(() => 0n), feesLastUpdated: t.bigint(), // Floor price metrics floorTick: t.integer(), floorPriceWei: t.bigint(), currentPriceWei: t.bigint(), floorDistanceBps: t.integer(), })); // Individual staking positions export const positions = onchainTable( 'positions', t => ({ id: t.text().primaryKey(), // Position ID from contract owner: t.hex().notNull(), share: t.real().notNull(), // Share as decimal (0-1) taxRate: t.real().notNull(), // Tax rate as decimal (e.g., 0.01 for 1%) - for display taxRateIndex: t.integer().notNull(), // Tax rate index from contract - source of truth kraikenDeposit: t.bigint().notNull(), stakeDeposit: t.bigint().notNull(), taxPaid: t .bigint() .notNull() .$default(() => 0n), snatched: t .integer() .notNull() .$default(() => 0), creationTime: t.bigint().notNull(), lastTaxTime: t.bigint().notNull(), status: t .text() .notNull() .$default(() => 'Active'), // "Active" or "Closed" createdAt: t.bigint().notNull(), closedAt: t.bigint(), totalSupplyInit: t.bigint().notNull(), totalSupplyEnd: t.bigint(), payout: t .bigint() .notNull() .$default(() => 0n), }), table => ({ ownerIdx: index().on(table.owner), statusIdx: index().on(table.status), taxRateIndexIdx: index().on(table.taxRateIndex), }) ); // Export decimal values for backward compatibility in event handlers // Maps index → decimal (e.g., TAX_RATES[0] = 0.01 for 1% yearly) export const TAX_RATES = TAX_RATE_OPTIONS.map(opt => opt.decimal); // Recenters - track LiquidityManager recenter events export const recenters = onchainTable('recenters', t => ({ id: t.text().primaryKey(), // block_logIndex format timestamp: t.bigint().notNull(), currentTick: t.integer().notNull(), isUp: t.boolean().notNull(), ethBalance: t.bigint(), // nullable - only from Scarcity/Abundance events outstandingSupply: t.bigint(), // nullable vwapTick: t.integer(), // nullable })); // Holders - track Kraiken token holders with cost basis for P&L export const holders = onchainTable( 'holders', t => ({ address: t.hex().primaryKey(), balance: t.bigint().notNull(), // Cost basis tracking (updated on swaps only, not wallet-to-wallet transfers) totalEthSpent: t .bigint() .notNull() .$default(() => 0n), // cumulative ETH spent buying KRK totalTokensAcquired: t .bigint() .notNull() .$default(() => 0n), // cumulative KRK received from buys }), table => ({ addressIdx: index().on(table.address), }) ); // Transaction history for wallet dashboard export const transactions = onchainTable( 'transactions', t => ({ id: t.text().primaryKey(), // txHash-logIndex holder: t.hex().notNull(), type: t.text().notNull(), // "buy" | "sell" | "stake" | "unstake" | "snatch_in" | "snatch_out" tokenAmount: t.bigint().notNull(), ethAmount: t .bigint() .notNull() .$default(() => 0n), timestamp: t.bigint().notNull(), blockNumber: t.integer().notNull(), txHash: t.hex().notNull(), }), table => ({ holderIdx: index().on(table.holder), timestampIdx: index().on(table.timestamp), }) ); // Helper constants export const STATS_ID = '0x01'; export const SECONDS_IN_HOUR = 3600;