harb/tests/e2e/usertest/marcus-degen.spec.ts

194 lines
9.1 KiB
TypeScript
Raw Normal View History

/* 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. */
2026-02-18 00:19:05 +01:00
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`);
2026-02-18 00:19:05 +01:00
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`);
2026-02-18 00:19:05 +01:00
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`);
2026-02-18 00:19:05 +01:00
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`);
2026-02-18 00:19:05 +01:00
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();
}
});
});