From 61a9fd7e588367efd1921183c75f7afe405db08f Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 5 Mar 2026 13:13:04 +0000 Subject: [PATCH] fix: evaluator: add sellKrk browser helper (uses sell widget from #456) (#461) Co-Authored-By: Claude Sonnet 4.6 --- scripts/harb-evaluator/helpers/swap.ts | 81 +++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/scripts/harb-evaluator/helpers/swap.ts b/scripts/harb-evaluator/helpers/swap.ts index 84f40d6..cde36c9 100644 --- a/scripts/harb-evaluator/helpers/swap.ts +++ b/scripts/harb-evaluator/helpers/swap.ts @@ -1,7 +1,8 @@ /** * Shared swap helpers for holdout scenarios. * - * buyKrk — drives the real get-krk page swap widget (UI path, requires #393 fix). + * buyKrk — drives the real get-krk page swap widget (UI path, requires #393 fix). + * sellKrk — drives the get-krk page sell widget UI (requires #456 sell tab). * sellAllKrk — submits approve + exactInputSingle directly via window.ethereum * (no UI widget — the Uniswap router handles the on-chain leg). */ @@ -167,6 +168,84 @@ export async function buyKrk(page: Page, ethAmount: string, opts?: BuyKrkOptions await page.screenshot({ path: `test-results/${screenshotPrefix}-after-buy.png` }); } +/** + * Navigate to the get-krk page, switch to Sell tab, fill KRK amount, click Sell. + * Wallet must already be connected. + * + * Drives the real sell widget UI (requires the #456 sell tab). + * + * If config is provided, queries WETH balance before and after the sell and + * returns the delta. Otherwise returns 0n (caller is responsible for verification). + * + * @param page - Playwright page with injected wallet + * @param amount - KRK amount to sell (as string). Use 'max' to click the Max button. + * @param screenshotPrefix - Optional prefix for screenshot filenames + * @param config - Optional config to query WETH received after sell + * @returns WETH received (balance diff) or 0n if config is not provided + */ +export async function sellKrk( + page: Page, + amount: string, + screenshotPrefix?: string, + config?: SellConfig, +): Promise { + console.log(`[swap] Selling ${amount} KRK via get-krk page sell widget...`); + await navigateSPA(page, '/app/get-krk'); + + const sellTab = page.getByTestId('swap-mode-sell'); + await expect(sellTab).toBeVisible({ timeout: 10_000 }); + await sellTab.click(); + + if (amount === 'max') { + const maxButton = page.locator('.max-button'); + await expect(maxButton).toBeVisible({ timeout: 5_000 }); + await maxButton.click(); + console.log('[swap] Clicked Max button'); + } else { + const sellInput = page.getByTestId('swap-sell-amount-input'); + await expect(sellInput).toBeVisible({ timeout: 5_000 }); + await sellInput.fill(amount); + console.log(`[swap] Filled sell amount: ${amount}`); + } + + const wethBefore = config ? await erc20BalanceOf(config.rpcUrl, WETH, config.accountAddress) : 0n; + + if (screenshotPrefix) { + await page.screenshot({ path: `test-results/${screenshotPrefix}-before-sell.png` }); + } + + const sellButton = page.getByTestId('swap-sell-button'); + await expect(sellButton).toBeVisible({ timeout: 5_000 }); + console.log('[swap] Clicking Sell KRK...'); + await sellButton.click(); + + // Button cycles: "Sell KRK" → "Approving…" / "Selling…" → "Sell KRK" + try { + await sellButton.filter({ hasText: /Approving…|Selling…/i }).waitFor({ state: 'visible', timeout: 5_000 }); + console.log('[swap] Sell in progress...'); + } catch { + // Sell completed before the transient state could be observed + console.log('[swap] Button state not observed (sell may have completed instantly)'); + } + await expect(sellButton).toHaveText('Sell KRK', { timeout: 60_000 }); + console.log('[swap] Sell completed'); + + if (screenshotPrefix) { + await page.screenshot({ path: `test-results/${screenshotPrefix}-after-sell.png` }); + } + + if (!config) return 0n; + + const wethAfter = await erc20BalanceOf(config.rpcUrl, WETH, config.accountAddress); + 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 ${wethReceived} WETH`); + } + return wethReceived; +} + /** * Query the current KRK balance, then approve the Uniswap router and swap * all KRK back to WETH via on-chain transactions submitted through the