harb/tests/e2e/usertest/sarah-yield-farmer.spec.ts
2026-02-18 00:19:05 +01:00

205 lines
11 KiB
TypeScript

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';
// Sarah uses Anvil account #2
const ACCOUNT_PRIVATE_KEY = '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a';
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('Sarah Park - Cautious Yield Farmer', () => {
test.beforeAll(async () => {
await resetChainState(STACK_RPC_URL);
await validateStackHealthy(STACK_CONFIG);
});
test('Sarah researches thoroughly before committing capital', async ({ browser }) => {
const report = createReport('Sarah Park');
const personaName = 'Sarah';
console.log(`[${personaName}] Starting test - Cautious yield farmer seeking sustainable returns...`);
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 (Reads Everything) ---
let pageStart = Date.now();
await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3_000);
await takeScreenshot(page, personaName, 'landing-page', report);
logObservation(personaName, 'Reading landing page carefully before connecting wallet', report);
logCopyFeedback(personaName, 'Landing page should explain "What is Harberger tax?" in simple terms', report);
recordPageVisit('Landing', page.url(), pageStart, report);
// --- Look for About/Docs FIRST ---
logObservation(personaName, 'Looking for About, Docs, or Team page before doing anything else...', report);
const hasAbout = await page.getByText(/about/i).first().isVisible().catch(() => false);
const hasDocs = await page.getByText(/docs|documentation/i).first().isVisible().catch(() => false);
const hasTeam = await page.getByText(/team/i).first().isVisible().catch(() => false);
if (!hasAbout && !hasDocs && !hasTeam) {
logCopyFeedback(personaName, 'MAJOR ISSUE: No About, Docs, or Team link visible. I need background info before trusting this.', report);
logObservation(personaName, 'Feeling uncertain - no clear educational resources or team transparency', report);
}
await takeScreenshot(page, personaName, 'looking-for-info', report);
// --- Check for Audit Badge ---
const auditVisible = await page.getByText(/audit/i).isVisible().catch(() => false);
if (!auditVisible) {
logCopyFeedback(personaName, 'No audit badge visible - this is a dealbreaker for me normally, but will test anyway', report);
logTokenomicsQuestion(personaName, 'Has this been audited by Certik, Trail of Bits, or similar?', report);
}
// --- Connect Wallet (Hesitantly) ---
logObservation(personaName, 'Deciding to connect wallet after reading available info...', report);
pageStart = Date.now();
await connectWallet(page);
await takeScreenshot(page, personaName, 'wallet-connected', report);
recordAction('Connect wallet', true, undefined, report);
logObservation(personaName, 'Wallet connected. Now checking the staking interface details.', report);
// --- Navigate to Stake Page to Learn ---
pageStart = Date.now();
await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`);
await page.waitForTimeout(3_000);
recordPageVisit('Stake (research)', page.url(), pageStart, report);
await takeScreenshot(page, personaName, 'stake-page-reading', report);
logObservation(personaName, 'Reading staking dashboard carefully - what are these tax rates about?', report);
logCopyFeedback(personaName, 'The info icon next to "Staking Dashboard" helps, but needs more detail on risks', report);
logTokenomicsQuestion(personaName, 'If I stake at 10% tax, what\'s my expected APY after taxes?', report);
logTokenomicsQuestion(personaName, 'What happens if I get snatched? Do I lose my principal or just my position?', report);
// --- Check Statistics Section ---
const statsSection = page.locator('.statistics-wrapper');
const statsVisible = await statsSection.isVisible().catch(() => false);
if (statsVisible) {
await takeScreenshot(page, personaName, 'statistics-analysis', report);
logObservation(personaName, 'Examining statistics - average tax rate, claimed slots, inflation rate', report);
logTokenomicsQuestion(personaName, 'How does the 7-day inflation compare to my expected staking returns?', report);
} else {
logCopyFeedback(personaName, 'Would be helpful to see protocol statistics and historical data', report);
}
// --- Mint ETH ---
pageStart = Date.now();
await page.goto(`${STACK_WEBAPP_URL}/app/#/cheats`);
await page.waitForTimeout(1_000);
recordPageVisit('Cheats', page.url(), pageStart, report);
logObservation(personaName, 'Using test environment to simulate before committing real funds', report);
await mintEth(page, STACK_RPC_URL, ACCOUNT_ADDRESS, '20');
recordAction('Mint 20 ETH', true, undefined, report);
// --- Small Test Purchase ---
logObservation(personaName, 'Starting with a small test purchase to understand the process', report);
await buyKrk(page, '0.05');
recordAction('Buy KRK with 0.05 ETH (test)', true, undefined, report);
await takeScreenshot(page, personaName, 'test-purchase-complete', report);
logObservation(personaName, 'Test purchase successful. Now buying more for actual staking.', report);
await page.waitForTimeout(2_000);
// --- Buy enough for staking (split to reduce slippage) ---
await buyKrk(page, '3.0');
recordAction('Buy KRK with 3.0 ETH total', true, undefined, report);
logObservation(personaName, 'Bought more KRK. Now ready to stake.', report);
await page.waitForTimeout(2_000);
// --- Navigate Back to Stake ---
pageStart = Date.now();
await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`);
await page.waitForTimeout(2_000);
recordPageVisit('Stake (attempt)', page.url(), pageStart, report);
await takeScreenshot(page, personaName, 'stake-form-before-fill', report);
logObservation(personaName, 'Examining the stake form - trying to understand tax rate implications', report);
logCopyFeedback(personaName, 'Tax rate dropdown needs explanation: "What tax rate should I choose?"', report);
logCopyFeedback(personaName, 'Would love a calculator: "Stake X at Y% tax = Z estimated APY"', report);
// --- Conservative Test Stake (High Tax for Safety) ---
logObservation(personaName, 'Choosing 15% tax rate to minimize snatch risk - prioritizing safety over yield', report);
logTokenomicsQuestion(personaName, 'Is 15% tax high enough to prevent snatching? What\'s the meta?', report);
try {
await attemptStake(page, '50', '15', personaName, report);
await takeScreenshot(page, personaName, 'conservative-stake-success', report);
logObservation(personaName, 'Stake successful! Now monitoring to see if position stays secure.', report);
recordAction('Stake 50 KRK at 15% tax (conservative)', true, undefined, report);
} catch (error: any) {
logObservation(personaName, `Stake failed: ${error.message}. This is confusing and frustrating.`, report);
logCopyFeedback(personaName, 'Error messages need to be clearer and suggest solutions', report);
await takeScreenshot(page, personaName, 'stake-error', report);
}
await page.waitForTimeout(3_000);
// --- Check Active Positions ---
await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`);
await page.waitForTimeout(2_000);
await takeScreenshot(page, personaName, 'checking-my-position', report);
const activePositions = page.locator('.active-positions-wrapper');
const myPositionVisible = await activePositions.isVisible().catch(() => false);
if (myPositionVisible) {
logObservation(personaName, 'Can see my active position. Would want notifications when something changes.', report);
logCopyFeedback(personaName, 'Need mobile notifications or email alerts for position activity (snatch attempts, tax due)', report);
} else {
logObservation(personaName, 'Can\'t see my position clearly - where is it? Confusing UX.', report);
logCopyFeedback(personaName, '"My Positions" section should be more prominent', report);
}
// --- Compare to Mental Model (Aave) ---
logObservation(personaName, 'Comparing this to Aave in my head - Aave is simpler but boring...', report);
logTokenomicsQuestion(personaName, 'Aave gives me 8% on USDC with zero snatch risk. Why should I use this instead?', report);
logCopyFeedback(personaName, 'Needs a "Why Kraiken?" section comparing to traditional staking/lending', report);
// --- Final Thoughts ---
await page.waitForTimeout(2_000);
await takeScreenshot(page, personaName, 'final-review', report);
report.overallSentiment = 'Interested but need more information before committing real funds. The Harberger tax mechanism is intriguing but confusing - I don\'t fully understand how to optimize my tax rate or what happens if I get snatched. UI is clean but lacks educational content for newcomers. Missing: audit badge, return calculator, risk disclosures, comparison to alternatives, mobile notifications. Would need to monitor my test stake for 1-2 weeks before scaling up. Compared to Aave (8% risk-free), this needs to offer 10-15% to justify the complexity and snatch risk. Verdict: Promising but not ready for my main capital yet.';
logObservation(personaName, report.overallSentiment, report);
} finally {
writeReport('sarah-park', report);
await context.close();
}
});
});