diff --git a/tests/e2e/01-acquire-and-stake.spec.ts b/tests/e2e/01-acquire-and-stake.spec.ts index 92c9f19..64cd87a 100644 --- a/tests/e2e/01-acquire-and-stake.spec.ts +++ b/tests/e2e/01-acquire-and-stake.spec.ts @@ -260,11 +260,15 @@ test.describe('Acquire & Stake', () => { } catch (e) { console.log('[TEST] Transaction may have completed instantly'); } - await page.waitForTimeout(3_000); - - // Verify staking position via GraphQL - console.log('[TEST] Verifying staking position via GraphQL...'); - const positions = await fetchPositions(request, ACCOUNT_ADDRESS); + // Poll for Ponder to index the staking transaction (Ponder has indexing latency) + console.log('[TEST] Polling GraphQL for staking position (Ponder indexing latency)...'); + let positions: Awaited> = []; + for (let attempt = 0; attempt < 15; attempt++) { + await page.waitForTimeout(2_000); + positions = await fetchPositions(request, ACCOUNT_ADDRESS); + if (positions.length > 0) break; + console.log(`[TEST] Ponder not yet indexed (attempt ${attempt + 1}/15), retrying...`); + } console.log(`[TEST] Found ${positions.length} position(s)`); expect(positions.length).toBeGreaterThan(0); diff --git a/tests/e2e/05-optimizer-integration.spec.ts b/tests/e2e/05-optimizer-integration.spec.ts index b37833f..14522ef 100644 --- a/tests/e2e/05-optimizer-integration.spec.ts +++ b/tests/e2e/05-optimizer-integration.spec.ts @@ -7,10 +7,9 @@ const STACK_RPC_URL = STACK_CONFIG.rpcUrl; // Solidity function selectors const GET_LIQUIDITY_PARAMS_SELECTOR = '0xbd53c0dc'; // getLiquidityParams() const POSITIONS_SELECTOR = '0xf86aafc0'; // positions(uint8) -// OptimizerV3 known bear-market parameters -const BEAR_ANCHOR_SHARE = 3n * 10n ** 17n; // 3e17 = 30% -const BEAR_ANCHOR_WIDTH = 100n; -const BEAR_DISCOVERY_DEPTH = 3n * 10n ** 17n; // 3e17 + +// Optimizer.sol invariants (capitalInefficiency + anchorShare = 1e18) +const ONE_ETHER = 10n ** 18n; // Position stages const STAGE_FLOOR = 0; @@ -62,7 +61,7 @@ test.describe('Optimizer Integration', () => { await validateStackHealthy(STACK_CONFIG); }); - test('OptimizerV3 proxy returns valid bear-market parameters', async () => { + test('Optimizer proxy returns valid parameters', async () => { const optimizerAddress = STACK_CONFIG.contracts.OptimizerProxy; if (!optimizerAddress) { console.log( @@ -80,16 +79,19 @@ test.describe('Optimizer Integration', () => { console.log(`[TEST] anchorWidth: ${params.anchorWidth}`); console.log(`[TEST] discoveryDepth: ${params.discoveryDepth}`); - // With no staking activity, OptimizerV3 should return bear-market defaults - expect(params.capitalInefficiency).toBe(0n); - expect(params.anchorShare).toBe(BEAR_ANCHOR_SHARE); - expect(params.anchorWidth).toBe(BEAR_ANCHOR_WIDTH); - expect(params.discoveryDepth).toBe(BEAR_DISCOVERY_DEPTH); + // Optimizer.sol invariants: + // capitalInefficiency + anchorShare == 1e18 + expect(params.capitalInefficiency + params.anchorShare).toBe(ONE_ETHER); + // discoveryDepth == anchorShare + expect(params.discoveryDepth).toBe(params.anchorShare); + // anchorWidth in [10, 80] + expect(params.anchorWidth).toBeGreaterThanOrEqual(10n); + expect(params.anchorWidth).toBeLessThanOrEqual(80n); - console.log('[TEST] OptimizerV3 returns correct bear-market parameters'); + console.log('[TEST] Optimizer returns valid parameters (invariants satisfied)'); }); - test('bootstrap positions reflect optimizer anchorWidth=100 parameter', async () => { + test('bootstrap positions reflect valid optimizer anchorWidth', async () => { const lmAddress = STACK_CONFIG.contracts.LiquidityManager; const optimizerAddress = STACK_CONFIG.contracts.OptimizerProxy; if (!optimizerAddress) { @@ -97,12 +99,11 @@ test.describe('Optimizer Integration', () => { return; } - // Read optimizer params - const params = await readLiquidityParams(optimizerAddress); - const anchorWidth = Number(params.anchorWidth); - console.log(`[TEST] Optimizer anchorWidth: ${anchorWidth}`); - - // Read anchor position from LM (created by bootstrap's recenter call) + // Read anchor position from LM (created by bootstrap's recenter call). + // Note: optimizer state may have changed since bootstrap (e.g. staking activity in + // earlier tests), so we don't read the *current* optimizer params here. Instead + // we reverse-calculate the anchorWidth that was in effect when recenter() ran and + // verify it falls within Optimizer.sol's valid range [10, 80]. const anchorResult = (await rpcCall('eth_call', [ { to: lmAddress, @@ -117,16 +118,25 @@ test.describe('Optimizer Integration', () => { console.log(`[TEST] Anchor position: ticks=[${tickLower}, ${tickUpper}], spread=${anchorSpread}`); - // Verify the anchor spread matches the optimizer formula: - // anchorSpacing = TICK_SPACING + (34 * anchorWidth * TICK_SPACING / 100) - // For anchorWidth=100: anchorSpacing = 200 + (34 * 100 * 200 / 100) = 200 + 6800 = 7000 - // Full anchor = 2 * anchorSpacing = 14000 ticks - const expectedSpacing = TICK_SPACING + (34 * anchorWidth * TICK_SPACING) / 100; - const expectedSpread = expectedSpacing * 2; - console.log(`[TEST] Expected anchor spread: ${expectedSpread} (anchorSpacing=${expectedSpacing})`); + // Reverse the formula to recover anchorWidth: + // anchorSpread = 2 * (TICK_SPACING + (34 * anchorWidth * TICK_SPACING / 100)) + // => anchorWidth = (anchorSpread / 2 - TICK_SPACING) * 100 / (34 * TICK_SPACING) + const halfSpread = anchorSpread / 2; + expect(halfSpread).toBeGreaterThan(TICK_SPACING); + const impliedAnchorWidth = Math.round(((halfSpread - TICK_SPACING) * 100) / (34 * TICK_SPACING)); + console.log(`[TEST] Implied anchorWidth from spread: ${impliedAnchorWidth}`); + + // Optimizer.sol constrains anchorWidth to [10, 80] + expect(impliedAnchorWidth).toBeGreaterThanOrEqual(10); + expect(impliedAnchorWidth).toBeLessThanOrEqual(80); + + // Confirm the implied anchorWidth reproduces the exact spread (no rounding error) + const expectedSpacing = TICK_SPACING + (34 * impliedAnchorWidth * TICK_SPACING) / 100; + const expectedSpread = expectedSpacing * 2; expect(anchorSpread).toBe(expectedSpread); - console.log('[TEST] Anchor spread matches optimizer anchorWidth=100 formula'); + + console.log(`[TEST] Anchor spread ${anchorSpread} corresponds to valid anchorWidth=${impliedAnchorWidth}`); }); test('all three positions have valid relative sizing', async () => {