/* eslint-disable no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles. */ import { expect, test } from '@playwright/test'; import { Wallet } from 'ethers'; import { createWalletContext } from '../../setup/wallet-provider'; import { getStackConfig, validateStackHealthy } from '../../setup/stack'; import { createReport, connectWallet, mintEth, buyKrk, takeScreenshot, logObservation, logCopyFeedback, logTokenomicsQuestion, recordPageVisit, recordAction, writeReport, attemptStake, resetChainState, } from './helpers'; // Marcus uses Anvil account #1 const ACCOUNT_PRIVATE_KEY = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; const ACCOUNT_ADDRESS = new Wallet(ACCOUNT_PRIVATE_KEY).address.toLowerCase(); const STACK_CONFIG = getStackConfig(); const STACK_RPC_URL = STACK_CONFIG.rpcUrl; const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl; test.describe('Marcus "Flash" Chen - Degen/MEV Hunter', () => { test.beforeAll(async () => { await resetChainState(STACK_RPC_URL); await validateStackHealthy(STACK_CONFIG); }); test('Marcus explores Kraiken with a critical eye', async ({ browser }) => { const report = createReport('Marcus Flash Chen'); const personaName = 'Marcus'; console.log(`[${personaName}] Starting test - Degen/MEV hunter looking for edge cases...`); const context = await createWalletContext(browser, { privateKey: ACCOUNT_PRIVATE_KEY, rpcUrl: STACK_RPC_URL, }); const page = await context.newPage(); page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`)); page.on('pageerror', error => console.log(`[BROWSER ERROR] ${error.message}`)); try { // --- Landing Page --- let pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(2_000); await takeScreenshot(page, personaName, 'landing-page', report); logObservation(personaName, 'Lands on app, immediately skeptical - what\'s the catch?', report); logCopyFeedback(personaName, 'Landing page needs "Audited by X" badge prominently displayed', report); logTokenomicsQuestion(personaName, 'What prevents someone from flash-loaning to manipulate VWAP?', report); recordPageVisit('Landing', page.url(), pageStart, report); // --- Connect Wallet Immediately --- pageStart = Date.now(); await connectWallet(page); await takeScreenshot(page, personaName, 'wallet-connected', report); recordAction('Connect wallet', true, undefined, report); logObservation(personaName, 'Connected wallet - now looking for contract addresses to verify on Basescan', report); // --- Check for Documentation/Audit Links --- pageStart = Date.now(); logObservation(personaName, 'Scrolling through UI looking for audit report link...', report); // Marcus would look for footer, about page, docs const hasAuditLink = await page.getByText(/audit/i).isVisible().catch(() => false); const hasDocsLink = await page.getByText(/docs|documentation/i).isVisible().catch(() => false); if (!hasAuditLink) { logCopyFeedback(personaName, 'CRITICAL: No visible audit link. Immediate red flag for degens.', report); } if (!hasDocsLink) { logObservation(personaName, 'No docs link visible - would need to find contracts manually', report); } await takeScreenshot(page, personaName, 'looking-for-docs', report); // --- Mint ETH (Cheats) --- pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/cheats`); await page.waitForTimeout(1_000); recordPageVisit('Cheats', page.url(), pageStart, report); await takeScreenshot(page, personaName, 'cheats-page', report); logObservation(personaName, 'Found cheats page - good for testing edge cases quickly', report); await mintEth(page, STACK_RPC_URL, ACCOUNT_ADDRESS, '50'); recordAction('Mint 50 ETH', true, undefined, report); // --- Test Small Swap First (Paranoid) --- pageStart = Date.now(); logObservation(personaName, 'Testing small swap first to check slippage behavior', report); await buyKrk(page, '0.01'); recordAction('Buy KRK with 0.01 ETH (test)', true, undefined, report); await takeScreenshot(page, personaName, 'small-swap-complete', report); logTokenomicsQuestion(personaName, 'What\'s the slippage on this tiny swap? Is three-position liquidity working?', report); await page.waitForTimeout(2_000); // --- Test Larger Swap --- logObservation(personaName, 'Now testing larger swap to probe liquidity depth', report); await buyKrk(page, '5'); recordAction('Buy KRK with 5 ETH', true, undefined, report); await takeScreenshot(page, personaName, 'large-swap-complete', report); logTokenomicsQuestion(personaName, 'Did I hit the discovery edge? What\'s the actual buy depth?', report); await page.waitForTimeout(5_000); // Reload page to ensure balance is fresh await page.reload(); await page.waitForTimeout(3_000); // --- Navigate to Stake Page --- pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/stake`); await page.waitForTimeout(4_000); recordPageVisit('Stake', page.url(), pageStart, report); await takeScreenshot(page, personaName, 'stake-page-initial', report); logObservation(personaName, 'Examining stake interface - looking for snatching mechanics explanation', report); logCopyFeedback(personaName, 'Tax rate selector needs tooltip: "Higher tax = harder to snatch, lower yield"', report); logTokenomicsQuestion(personaName, 'What\'s the minimum profitable tax spread for snatching? Need a calculator.', report); // --- Attempt Low Tax Stake (Bait) --- logObservation(personaName, 'Staking at 2% tax intentionally - testing if someone can snatch me', report); try { await attemptStake(page, '100', '5', personaName, report); await takeScreenshot(page, personaName, 'low-tax-stake-success', report); logObservation(personaName, 'Stake worked at 2% - now waiting to see if I get snatched...', report); } catch (error: any) { logObservation(personaName, `Stake failed: ${error.message}. UI needs better error messages.`, report); await takeScreenshot(page, personaName, 'low-tax-stake-failed', report); } await page.waitForTimeout(3_000); // --- Try to Snatch Another Position (if visible) --- logObservation(personaName, 'Scrolling through active positions looking for snatch targets...', report); await page.goto(`${STACK_WEBAPP_URL}/app/stake`); await page.waitForTimeout(2_000); await takeScreenshot(page, personaName, 'looking-for-snatch-targets', report); const activePositions = page.locator('.active-positions-list .collapse-active'); const positionCount = await activePositions.count(); logObservation(personaName, `Found ${positionCount} active positions. Checking tax rates for snatch opportunities.`, report); if (positionCount > 0) { logTokenomicsQuestion(personaName, 'What\'s the gas cost vs profit on snatching? Need ROI calculator.', report); } else { logObservation(personaName, 'No other positions visible yet - can\'t test snatching mechanics', report); } // --- Check Statistics --- pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/stake`); await page.waitForTimeout(1_000); const statsVisible = await page.getByText('Statistics').isVisible().catch(() => false); if (statsVisible) { await takeScreenshot(page, personaName, 'statistics-section', report); logObservation(personaName, 'Checking average tax rate and claimed slots - looking for meta trends', report); logTokenomicsQuestion(personaName, 'What\'s the Nash equilibrium tax rate? Is there a dominant strategy?', report); } // --- Final Thoughts --- await page.waitForTimeout(2_000); await takeScreenshot(page, personaName, 'final-dashboard', report); report.overallSentiment = 'Intrigued but cautious. Mechanics are novel and create genuine PvP opportunity. Would need to see audit, verify contracts on Basescan, and test snatching profitability in production. Missing: clear contract addresses, audit badge, slippage calculator, snatching ROI tool. Three-position liquidity is interesting - need to verify it actually works under manipulation attempts. Would allocate small bag ($2-5k) to test in production, but not going all-in until proven safe.'; logObservation(personaName, report.overallSentiment, report); } finally { writeReport('marcus-flash-chen', report); await context.close(); } }); });