fix: LiveStats: remove floor price from landing, fix ETH reserve data pipeline, strip browser RPC (#196)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
openhands 2026-02-23 21:57:13 +00:00
parent d398c7667d
commit 7219f1b21c
4 changed files with 64 additions and 94 deletions

View file

@ -341,6 +341,51 @@ export async function recordEthReserveSnapshot(context: StatsContext, timestamp:
});
}
// WETH address is identical across Base mainnet, Base Sepolia, and local Anvil fork
const WETH_ADDRESS = (process.env.WETH_ADDRESS || '0x4200000000000000000000000000000000000006') as `0x${string}`;
// Minimal ERC-20 ABI — only balanceOf is needed
const erc20BalanceOfAbi = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'account', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }],
},
] as const;
/**
* Read WETH balance of the Uniswap V3 pool via Ponder's cached client and
* persist it as `lastEthReserve` in the stats row.
*
* Call this from any event handler where a trade or stake changes the pool
* balance (Kraiken:Transfer buys/sells, Stake:PositionCreated/Removed).
* EthScarcity/EthAbundance handlers already receive the balance in event args
* and update `lastEthReserve` directly via `updateReserveStats()`.
*/
export async function updateEthReserve(context: StatsContext, poolAddress: `0x${string}`) {
let wethBalance: bigint;
try {
wethBalance = await context.client.readContract({
abi: erc20BalanceOfAbi,
address: WETH_ADDRESS,
functionName: 'balanceOf',
args: [poolAddress],
});
} catch (error) {
const logger = getLogger(context);
logger.warn('[stats.updateEthReserve] Failed to read WETH balance', error);
return;
}
if (wethBalance === 0n) return; // Pool not yet seeded — don't overwrite a real value with 0
await context.db.update(stats, { id: STATS_ID }).set({
lastEthReserve: wethBalance,
});
}
export async function refreshMinStake(context: StatsContext, statsData?: Awaited<ReturnType<typeof ensureStatsExists>>) {
let currentStats = statsData;
if (!currentStats) {

View file

@ -9,6 +9,7 @@ import {
checkBlockHistorySufficient,
RING_BUFFER_SEGMENTS,
refreshMinStake,
updateEthReserve,
} from './helpers/stats';
import { validateContractVersion } from './helpers/version';
@ -138,6 +139,9 @@ ponder.on('Kraiken:Transfer', async ({ event, context }) => {
blockNumber: Number(event.block.number),
txHash: event.transaction.hash,
});
// Update ETH reserve from pool WETH balance — buys/sells shift pool ETH
await updateEthReserve(context, POOL_ADDRESS);
}
}

View file

@ -7,9 +7,13 @@ import {
refreshOutstandingStake,
updateHourlyData,
checkBlockHistorySufficient,
updateEthReserve,
} from './helpers/stats';
import type { StatsContext } from './helpers/stats';
// Pool address — staking/unstaking events keep lastEthReserve fresh alongside buy/sell events
const POOL_ADDRESS = (process.env.POOL_ADDRESS || '0x1f69cbfc7d3529a4fb4eadf18ec5644b2603b5ab') as `0x${string}`;
const ZERO = 0n;
async function getKraikenTotalSupply(context: StatsContext) {
@ -66,6 +70,8 @@ ponder.on('Stake:PositionCreated', async ({ event, context }) => {
await refreshOutstandingStake(context);
await markPositionsUpdated(context, event.block.timestamp);
// Keep ETH reserve fresh — stake events may coincide with pool activity
await updateEthReserve(context, POOL_ADDRESS);
});
ponder.on('Stake:PositionRemoved', async ({ event, context }) => {
@ -100,6 +106,8 @@ ponder.on('Stake:PositionRemoved', async ({ event, context }) => {
await refreshOutstandingStake(context);
await markPositionsUpdated(context, event.block.timestamp);
// Keep ETH reserve fresh — unstake events may coincide with pool activity
await updateEthReserve(context, POOL_ADDRESS);
if (checkBlockHistorySufficient(context, event)) {
await updateHourlyData(context, event.block.timestamp);