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; // ubi, minted, burned, tax 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), totalUbiClaimed: 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), ubiClaimedLastWeek: t .bigint() .notNull() .$default(() => 0n), ubiClaimedLastDay: t .bigint() .notNull() .$default(() => 0n), ubiClaimedNextHourProjected: 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(), })); // ETH reserve history - tracks ethBalance over time for 7d growth calculation export const ethReserveHistory = onchainTable('ethReserveHistory', t => ({ id: t.text().primaryKey(), // block_logIndex format timestamp: t.bigint().notNull(), ethBalance: t.bigint().notNull(), })); // Fee history - tracks fees earned over time for 7d totals export const feeHistory = onchainTable('feeHistory', t => ({ id: t.text().primaryKey(), // block_logIndex format timestamp: t.bigint().notNull(), ethFees: t.bigint().notNull(), krkFees: t.bigint().notNull(), })); // 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 export const holders = onchainTable( 'holders', t => ({ address: t.hex().primaryKey(), balance: t.bigint().notNull(), }), table => ({ addressIdx: index().on(table.address), }) ); // Helper constants export const STATS_ID = '0x01'; export const SECONDS_IN_HOUR = 3600;