Merge pull request 'fix: LiveStats: remove floor price from landing, fix ETH reserve data pipeline, strip browser RPC (#196)' (#198) from fix/issue-196 into master
This commit is contained in:
commit
a321849ff6
6 changed files with 117 additions and 100 deletions
|
|
@ -4,7 +4,7 @@
|
|||
<span class="live-dot" :class="{ 'live-dot-error': error }"></span>
|
||||
<span class="live-text">Live</span>
|
||||
</div>
|
||||
<div class="stats-grid" :class="{ 'has-floor': showFloorPrice }">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item" :class="{ 'stat-changed': changedStats.has('ethReserve') }">
|
||||
<div class="stat-label">ETH Reserve</div>
|
||||
<div class="stat-value">{{ ethReserveDisplay }}</div>
|
||||
|
|
@ -13,18 +13,13 @@
|
|||
<svg v-if="ethReserveSpark.length > 1" class="sparkline" viewBox="0 0 80 24" preserveAspectRatio="none">
|
||||
<polyline :points="toSvgPoints(ethReserveSpark)" fill="none" stroke="rgba(96,165,250,0.5)" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span v-else-if="stats" class="spark-placeholder">Gathering data...</span>
|
||||
</div>
|
||||
<div class="stat-item" :class="{ 'stat-changed': changedStats.has('ethPerToken') }">
|
||||
<div class="stat-label">ETH / Token</div>
|
||||
<div class="stat-value">{{ ethPerTokenDisplay }}</div>
|
||||
<div v-if="ethPerTokenSecondary" class="stat-secondary">{{ ethPerTokenSecondary }}</div>
|
||||
</div>
|
||||
<div v-if="showFloorPrice" class="stat-item" :class="{ 'stat-changed': changedStats.has('floorPrice') }">
|
||||
<div class="stat-label">Floor Price</div>
|
||||
<div class="stat-value">{{ floorPriceDisplay }}</div>
|
||||
<div v-if="floorPriceSecondary" class="stat-secondary">{{ floorPriceSecondary }}</div>
|
||||
<div v-if="floorDistanceText" class="floor-distance">{{ floorDistanceText }}</div>
|
||||
</div>
|
||||
<div class="stat-item" :class="{ 'stat-changed': changedStats.has('supply') }">
|
||||
<div class="stat-label">Supply (7d)</div>
|
||||
<div class="stat-value">{{ totalSupply }}</div>
|
||||
|
|
@ -32,6 +27,7 @@
|
|||
<svg v-if="supplySpark.length > 1" class="sparkline" viewBox="0 0 80 24" preserveAspectRatio="none">
|
||||
<polyline :points="toSvgPoints(supplySpark)" fill="none" stroke="rgba(74,222,128,0.5)" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span v-else-if="stats" class="spark-placeholder">Gathering data...</span>
|
||||
</div>
|
||||
<div class="stat-item" :class="{ 'stat-changed': changedStats.has('holders') }">
|
||||
<div class="stat-label">Holders</div>
|
||||
|
|
@ -40,6 +36,7 @@
|
|||
<svg v-if="holdersSpark.length > 1" class="sparkline" viewBox="0 0 80 24" preserveAspectRatio="none">
|
||||
<polyline :points="toSvgPoints(holdersSpark)" fill="none" stroke="rgba(251,191,36,0.5)" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round" />
|
||||
</svg>
|
||||
<span v-else-if="stats" class="spark-placeholder">Gathering data...</span>
|
||||
</div>
|
||||
<div class="stat-item" :class="{ 'pulse': isRecentRebalance, 'stat-changed': changedStats.has('rebalances') }">
|
||||
<div class="stat-label">Rebalances</div>
|
||||
|
|
@ -76,10 +73,6 @@ interface Stats {
|
|||
burnedLastWeek: string;
|
||||
netSupplyChangeWeek: string;
|
||||
ethReserveGrowthBps: number | null;
|
||||
feesEarned7dEth: string | null;
|
||||
floorPriceWei: string | null;
|
||||
floorDistanceBps: number | null;
|
||||
currentPriceWei: string | null;
|
||||
ringBuffer: string[] | null;
|
||||
ringBufferPointer: number | null;
|
||||
}
|
||||
|
|
@ -362,35 +355,6 @@ const totalSupply = computed(() => {
|
|||
return `${(supply / 1000).toFixed(1)}K KRK`;
|
||||
});
|
||||
|
||||
// Floor price: only show when data is available
|
||||
const showFloorPrice = computed(() => {
|
||||
return !!(stats.value?.floorPriceWei && stats.value.floorPriceWei !== '0');
|
||||
});
|
||||
|
||||
const floorPriceAmount = computed(() => {
|
||||
if (!showFloorPrice.value || !stats.value?.floorPriceWei) return null;
|
||||
return weiToEth(stats.value.floorPriceWei);
|
||||
});
|
||||
|
||||
const floorPriceDisplay = computed(() => {
|
||||
if (floorPriceAmount.value == null) return '—';
|
||||
const eth = floorPriceAmount.value;
|
||||
if (ethUsdPrice.value) return formatUsd(eth * ethUsdPrice.value);
|
||||
return formatSmallEth(eth);
|
||||
});
|
||||
|
||||
const floorPriceSecondary = computed((): string | null => {
|
||||
if (floorPriceAmount.value == null || !ethUsdPrice.value) return null;
|
||||
return `(${formatSmallEth(floorPriceAmount.value)})`;
|
||||
});
|
||||
|
||||
const floorDistanceText = computed((): string | null => {
|
||||
if (!stats.value || stats.value.floorDistanceBps == null) return null;
|
||||
const distPct = Number(stats.value.floorDistanceBps) / 100;
|
||||
const aboveBelow = distPct >= 0 ? 'above' : 'below';
|
||||
return `(${Math.abs(distPct).toFixed(0)}% ${aboveBelow})`;
|
||||
});
|
||||
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const endpoint = `${window.location.origin}/api/graphql`;
|
||||
|
|
@ -413,10 +377,6 @@ async function fetchStats() {
|
|||
burnedLastWeek
|
||||
netSupplyChangeWeek
|
||||
ethReserveGrowthBps
|
||||
feesEarned7dEth
|
||||
floorPriceWei
|
||||
floorDistanceBps
|
||||
currentPriceWei
|
||||
ringBuffer
|
||||
ringBufferPointer
|
||||
}
|
||||
|
|
@ -434,47 +394,6 @@ async function fetchStats() {
|
|||
|
||||
if (data.data?.statss?.items?.[0]) {
|
||||
const s = data.data.statss.items[0];
|
||||
// If ETH reserve is 0 from Ponder (EthScarcity/EthAbundance events never emitted),
|
||||
// read WETH balance of the Uniswap V3 pool directly via RPC
|
||||
if (s.lastEthReserve === '0' || !s.lastEthReserve) {
|
||||
try {
|
||||
const rpc = `${window.location.origin}/api/rpc`;
|
||||
const deployResp = await fetch(`${window.location.origin}/app/deployments-local.json`);
|
||||
if (deployResp.ok) {
|
||||
const deployments = await deployResp.json();
|
||||
const krkAddr = deployments.contracts?.Kraiken;
|
||||
if (krkAddr) {
|
||||
const wethAddr = '0x4200000000000000000000000000000000000006';
|
||||
const factoryAddr = '0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24';
|
||||
const fee = 10000; // 1% fee tier
|
||||
|
||||
// Step 1: factory.getPool(weth, kraiken, fee) → pool address
|
||||
// selector: 0x1698ee82
|
||||
const wethPad = wethAddr.slice(2).padStart(64, '0');
|
||||
const krkPad = krkAddr.slice(2).padStart(64, '0');
|
||||
const feePad = fee.toString(16).padStart(64, '0');
|
||||
const poolRes = await fetch(rpc, { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call',
|
||||
params: [{ to: factoryAddr, data: '0x1698ee82' + wethPad + krkPad + feePad }, 'latest'] }) });
|
||||
const poolJson = await poolRes.json();
|
||||
const poolAddr = '0x' + (poolJson.result || '').slice(26);
|
||||
|
||||
if (poolAddr.length === 42 && poolAddr !== '0x' + '0'.repeat(40)) {
|
||||
// Step 2: weth.balanceOf(pool) → ETH reserve in pool
|
||||
const poolPad = poolAddr.slice(2).padStart(64, '0');
|
||||
const balRes = await fetch(rpc, { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'eth_call',
|
||||
params: [{ to: wethAddr, data: '0x70a08231' + poolPad }, 'latest'] }) });
|
||||
const balJson = await balRes.json();
|
||||
const wethBal = BigInt(balJson.result || '0x0');
|
||||
if (wethBal > 0n) {
|
||||
s.lastEthReserve = wethBal.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { /* ignore RPC fallback errors */ }
|
||||
}
|
||||
// Detect changed fields for flash animation
|
||||
const prev = stats.value;
|
||||
if (prev) {
|
||||
|
|
@ -487,7 +406,6 @@ async function fetchStats() {
|
|||
changed.add('supply');
|
||||
changed.add('ethPerToken');
|
||||
}
|
||||
if (s.floorPriceWei !== prev.floorPriceWei) changed.add('floorPrice');
|
||||
if (s.holderCount !== prev.holderCount) changed.add('holders');
|
||||
if (s.recentersLastWeek !== prev.recentersLastWeek) changed.add('rebalances');
|
||||
if (changed.size > 0) {
|
||||
|
|
@ -539,9 +457,6 @@ onUnmounted(() => {
|
|||
margin: 0 auto
|
||||
padding: 0 32px
|
||||
|
||||
&.has-floor
|
||||
max-width: 1020px
|
||||
|
||||
@media (min-width: 640px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
|
||||
|
|
@ -549,9 +464,6 @@ onUnmounted(() => {
|
|||
grid-template-columns: repeat(3, 1fr)
|
||||
gap: 32px
|
||||
|
||||
&.has-floor
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
|
||||
.stat-item
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
|
@ -608,10 +520,11 @@ onUnmounted(() => {
|
|||
color: rgba(240, 240, 240, 0.45)
|
||||
letter-spacing: 0.3px
|
||||
|
||||
.floor-distance
|
||||
.spark-placeholder
|
||||
font-size: 11px
|
||||
color: rgba(240, 240, 240, 0.5)
|
||||
color: rgba(240, 240, 240, 0.3)
|
||||
letter-spacing: 0.3px
|
||||
margin-top: 4px
|
||||
|
||||
.pulse
|
||||
animation: pulse-glow 2s ease-in-out infinite
|
||||
|
|
|
|||
57
package-lock.json
generated
57
package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "workspace",
|
||||
"name": "harb-worktree-196",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
@ -258,6 +258,7 @@
|
|||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
|
|
@ -1101,6 +1102,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
|
|
@ -1141,6 +1143,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
|
|
@ -4799,6 +4802,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz",
|
||||
"integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
|
|
@ -5861,6 +5865,7 @@
|
|||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
@ -6206,6 +6211,7 @@
|
|||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
@ -6503,6 +6509,7 @@
|
|||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
@ -7024,6 +7031,7 @@
|
|||
"integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^8.13.0",
|
||||
"eslint-visitor-keys": "^4.2.0",
|
||||
|
|
@ -7478,6 +7486,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
}
|
||||
|
|
@ -7573,6 +7582,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
|
|
@ -7674,6 +7684,7 @@
|
|||
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.56.0",
|
||||
"@typescript-eslint/types": "8.56.0",
|
||||
|
|
@ -8468,6 +8479,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz",
|
||||
"integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eventemitter3": "5.0.1",
|
||||
"mipd": "0.0.7",
|
||||
|
|
@ -9069,6 +9081,7 @@
|
|||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
@ -9271,6 +9284,7 @@
|
|||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -9862,6 +9876,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -9945,6 +9960,7 @@
|
|||
"integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
|
|
@ -10219,6 +10235,7 @@
|
|||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
|
|
@ -10721,6 +10738,7 @@
|
|||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
||||
"integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.7.0"
|
||||
}
|
||||
|
|
@ -10757,6 +10775,7 @@
|
|||
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
|
||||
"integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"uncrypto": "^0.1.3"
|
||||
}
|
||||
|
|
@ -11273,6 +11292,7 @@
|
|||
"resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.17.tgz",
|
||||
"integrity": "sha512-TOOURki4G7sD1wDCjj7NfLaXZZ49dFOeEb5y39IXpb8p0hRzVvfvzZHOi5JcT+PpyAbi/Y+lxPb8eTag2WYH8w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ecies/ciphers": "^0.2.5",
|
||||
"@noble/ciphers": "^1.3.0",
|
||||
|
|
@ -11702,6 +11722,7 @@
|
|||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
|
|
@ -11778,6 +11799,7 @@
|
|||
"integrity": "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
|
|
@ -12195,7 +12217,8 @@
|
|||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
|
||||
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
|
|
@ -12316,6 +12339,7 @@
|
|||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
|
|
@ -13109,6 +13133,7 @@
|
|||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
|
||||
"integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
|
|
@ -13190,6 +13215,7 @@
|
|||
"integrity": "sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
|
|
@ -13265,6 +13291,7 @@
|
|||
"integrity": "sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"webidl-conversions": "^7.0.0",
|
||||
"whatwg-mimetype": "^3.0.0"
|
||||
|
|
@ -14308,6 +14335,7 @@
|
|||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
|
|
@ -15307,6 +15335,7 @@
|
|||
"integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@acemir/cssom": "^0.9.28",
|
||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||
|
|
@ -15629,6 +15658,7 @@
|
|||
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
|
||||
"devOptional": true,
|
||||
"license": "MPL-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
|
|
@ -16054,13 +16084,15 @@
|
|||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
|
||||
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
|
|
@ -18356,6 +18388,7 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -18380,6 +18413,7 @@
|
|||
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
|
|
@ -18505,6 +18539,7 @@
|
|||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
|
@ -18725,6 +18760,7 @@
|
|||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz",
|
||||
"integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
|
|
@ -19312,6 +19348,7 @@
|
|||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
|
||||
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.4.1",
|
||||
|
|
@ -19784,7 +19821,8 @@
|
|||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
|
||||
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tailwindcss-animate": {
|
||||
"version": "1.0.7",
|
||||
|
|
@ -20276,6 +20314,7 @@
|
|||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -20661,6 +20700,7 @@
|
|||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
|
|
@ -20716,6 +20756,7 @@
|
|||
"resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz",
|
||||
"integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"derive-valtio": "0.1.0",
|
||||
"proxy-compare": "2.6.0",
|
||||
|
|
@ -20767,6 +20808,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@noble/curves": "1.9.1",
|
||||
"@noble/hashes": "1.8.0",
|
||||
|
|
@ -20914,6 +20956,7 @@
|
|||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
|
|
@ -21191,6 +21234,7 @@
|
|||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
|
||||
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.28",
|
||||
"@vue/compiler-sfc": "3.5.28",
|
||||
|
|
@ -21219,6 +21263,7 @@
|
|||
"integrity": "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"eslint-scope": "^8.2.0 || ^9.0.0",
|
||||
|
|
@ -21527,6 +21572,7 @@
|
|||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
|
@ -21714,6 +21760,7 @@
|
|||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
|
|
|||
2
services/ponder/package-lock.json
generated
2
services/ponder/package-lock.json
generated
|
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
},
|
||||
"../../kraiken-lib": {
|
||||
"version": "0.2.0",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.9.10",
|
||||
"graphql": "^16.8.1",
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue