274 lines
6.4 KiB
TypeScript
274 lines
6.4 KiB
TypeScript
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<string[]>()
|
|
.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;
|