From 70b9709a8b3e409705e283f802236a4debd09c6f Mon Sep 17 00:00:00 2001 From: guoyunfeng Date: Wed, 21 Oct 2026 23:18:56 +0800 Subject: [PATCH] fix(indexer): remove scaffold random writes to pool_snapshots (closes #5) --- README.md | 2 +- services/indexer/src/watcher.ts | 21 --------------------- services/indexer/test/watcher.test.ts | 11 ++++++++++- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index bf31528..99347f8 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ Phase 2 implements the observation and persistence path: - `repositories/vault-repository.ts` upserts vault metadata and appends decoded events, deduplicating on the Soroban event id so a replay is idempotent. - `config.ts` reads the RPC, contract, and Supabase configuration. -Still planned: a separate processor/handler split, reconciliation, and stronger replay guarantees. The watcher also retains a scaffold-only `pool_snapshots` write that the existing API route and dashboard still consume; replacing it with real vault state requires contract state reads (Phase 5). +Still planned: a separate processor/handler split, reconciliation, and stronger replay guarantees. Retained scaffold `pool_snapshots` writes of randomly generated TVL and APY have been removed in accordance with the project's "no fake implementations" rule; real vault statistics will be populated once contract state reads and NAV feeds are wired in Phase 5. ## Frontend Architecture diff --git a/services/indexer/src/watcher.ts b/services/indexer/src/watcher.ts index 5c8fdfa..8780e7a 100644 --- a/services/indexer/src/watcher.ts +++ b/services/indexer/src/watcher.ts @@ -1,7 +1,6 @@ import type { SupabaseClient } from '@supabase/supabase-js'; import { Server, type Api } from '@stellar/stellar-sdk/rpc'; import { - DATABASE_TABLES, INDEXER_DEFAULTS, INDEXER_EVENTS_STREAM, } from '@yieldanchor/constants'; @@ -68,23 +67,6 @@ async function projectEvents( await insertVaultEvents(supabase, events); } -/** - * Scaffold-only behaviour retained from the Phase 0 poller: the read API and - * web dashboard still consume `pool_snapshots`. Replacing this with real vault - * state requires contract state reads, which are Phase 5 work. - */ -async function recordScaffoldSnapshot(supabase: SupabaseClient): Promise { - const tvl = Math.floor(Math.random() * 10_000_000) / 100; - const dynamicApy = (5 + Math.random() * 5).toFixed(2); - await supabase.from(DATABASE_TABLES.poolSnapshots).insert([ - { - tvl, - dynamic_apy: Number(dynamicApy), - timestamp: new Date().toISOString(), - }, - ]); -} - export async function startWatcher(): Promise { const config = loadConfig(); const supabase = createSupabaseClient(config); @@ -172,9 +154,6 @@ export async function startWatcher(): Promise { ); } - if (supabase) { - await recordScaffoldSnapshot(supabase); - } } catch (error) { console.error('Poller error', error); } finally { diff --git a/services/indexer/test/watcher.test.ts b/services/indexer/test/watcher.test.ts index 30e234d..daa7829 100644 --- a/services/indexer/test/watcher.test.ts +++ b/services/indexer/test/watcher.test.ts @@ -10,6 +10,7 @@ import { const mocks = vi.hoisted(() => ({ hasDatabase: false, + fromSpy: vi.fn(), getEvents: vi.fn(), getLatestLedger: vi.fn(), loadCheckpoint: vi.fn(), @@ -31,7 +32,7 @@ vi.mock('../src/config.js', () => ({ loadConfig: () => ({ contractId: CONTRACT, rpcUrl: 'https://rpc.invalid' }), createSupabaseClient: () => mocks.hasDatabase - ? { from: () => ({ insert: async () => ({ error: null }) }) } + ? { from: mocks.fromSpy } : null, })); @@ -113,6 +114,7 @@ describe('watcher persistence logging', () => { vi.spyOn(console, 'warn').mockImplementation(() => undefined); vi.spyOn(console, 'error').mockImplementation(() => undefined); mocks.hasDatabase = false; + mocks.fromSpy.mockReturnValue({ insert: vi.fn().mockResolvedValue({ error: null }) }); mocks.loadCheckpoint.mockResolvedValue(null); mocks.saveCheckpoint.mockResolvedValue(undefined); mocks.insertVaultEvents.mockResolvedValue(undefined); @@ -183,4 +185,11 @@ describe('watcher persistence logging', () => { ); expect(mocks.saveCheckpoint).not.toHaveBeenCalled(); }); + + it('does not write fabricated random snapshots to pool_snapshots', async () => { + mocks.hasDatabase = true; + await startWatcher(); + + expect(mocks.fromSpy).not.toHaveBeenCalledWith('pool_snapshots'); + }); });