From 74a043262d02b1a6369aaa6af88bf13c0f135e5a Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 3 Mar 2026 19:45:46 +0000 Subject: [PATCH] feat(holdout): Add reasonable slippage assertion to always-leave scenario - Modified sellAllKrk helper to return WETH delta received - Added assertion: WETH received >= 90% of ETH spent (0.09 ETH minimum) - Added log showing actual slippage percentage - This proves 'always leave' with reasonable slippage, not just exit ability --- scripts/harb-evaluator/helpers/swap.ts | 10 +++++++--- .../scenarios/sovereign-exit/always-leave.spec.ts | 12 ++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/harb-evaluator/helpers/swap.ts b/scripts/harb-evaluator/helpers/swap.ts index 86e27a8..85e4627 100644 --- a/scripts/harb-evaluator/helpers/swap.ts +++ b/scripts/harb-evaluator/helpers/swap.ts @@ -119,8 +119,10 @@ export async function buyKrk(page: Page, ethAmount: string): Promise { * Logs a warning if the WETH balance does not increase after the swap, which * indicates the pool returned 0 output (possible with amountOutMinimum: 0n on * a partially-drained pool). + * + * @returns The WETH delta (wethAfter - wethBefore) received from the swap. */ -export async function sellAllKrk(page: Page, config: SellConfig): Promise { +export async function sellAllKrk(page: Page, config: SellConfig): Promise { const krkBalance = await erc20BalanceOf(config.rpcUrl, config.krkAddress, config.accountAddress); if (krkBalance === 0n) throw new Error('sellAllKrk: KRK balance is 0 — nothing to sell'); @@ -175,9 +177,11 @@ export async function sellAllKrk(page: Page, config: SellConfig): Promise console.log('[swap] Swap mined'); const wethAfter = await erc20BalanceOf(config.rpcUrl, WETH, config.accountAddress); - if (wethAfter <= wethBefore) { + const wethReceived = wethAfter - wethBefore; + if (wethReceived <= 0n) { console.warn('[swap] WARNING: WETH balance did not increase after sell — pool may have returned 0 output'); } else { - console.log(`[swap] Received ${wethAfter - wethBefore} WETH`); + console.log(`[swap] Received ${wethReceived} WETH`); } + return wethReceived; } diff --git a/scripts/harb-evaluator/scenarios/sovereign-exit/always-leave.spec.ts b/scripts/harb-evaluator/scenarios/sovereign-exit/always-leave.spec.ts index cc10576..0351b8c 100644 --- a/scripts/harb-evaluator/scenarios/sovereign-exit/always-leave.spec.ts +++ b/scripts/harb-evaluator/scenarios/sovereign-exit/always-leave.spec.ts @@ -12,7 +12,7 @@ * so no collision occurs. */ import { expect, test } from '@playwright/test'; -import { Wallet } from 'ethers'; +import { parseEther, Wallet } from 'ethers'; import { createWalletContext } from '../../../../tests/setup/wallet-provider'; import { getStackConfig } from '../../../../tests/setup/stack'; import { connectWallet, getKrkBalance } from '../../helpers/wallet'; @@ -55,7 +55,7 @@ test('I can always leave', async ({ browser }) => { console.log('[TEST] ✅ KRK received'); // ── 4. Sell all KRK back (sovereign exit) ──────────────────────────── - await sellAllKrk(page, { + const wethReceived = await sellAllKrk(page, { rpcUrl: config.rpcUrl, krkAddress: config.contracts.Kraiken, accountAddress: ACCOUNT_ADDRESS, @@ -66,6 +66,14 @@ test('I can always leave', async ({ browser }) => { console.log(`[TEST] KRK balance after sell: ${krkAfterSell}`); expect(krkAfterSell).toBeLessThan(krkAfterBuy); console.log('[TEST] ✅ Sovereign exit confirmed: KRK sold back to WETH'); + + // ── 6. Assert reasonable slippage (at least 90% of ETH spent) ───────── + const ethSpent = parseEther('0.1'); + const minExpected = parseEther('0.09'); // 90% of 0.1 ETH + expect(wethReceived).toBeGreaterThanOrEqual(minExpected); + const slippagePercent = ((Number(wethReceived) / Number(ethSpent)) * 100).toFixed(2); + console.log(`[TEST] ✅ Reasonable slippage: received ${wethReceived} WETH for 0.1 ETH spent (${slippagePercent}%)`); + } finally { await ctx.close(); }