AGENTS.md watermarks refreshed to HEAD (62ba6f2).
Content updates:
- root AGENTS.md: added Docker/LXD notes (apparmor=unconfined, umami port 3001),
viem v2 slot0 array pattern to Key Patterns
- services/ponder/AGENTS.md: documented LM_ADDRESS, POOL_ADDRESS, and
MINIMUM_BLOCKS_FOR_RINGBUFFER env vars; added zero-stats troubleshooting note
- web-app/AGENTS.md: added viem v2 slot0 array compat section
Grooming: no open issues.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.7 KiB
Web App - Agent Guide
Vue 3 + TypeScript staking interface for KRAIKEN, enabling users to stake tokens, manage positions, and interact with Harberger-tax mechanics.
Technology Snapshot
- Vue 3 (Composition API) with TypeScript and Vite toolchain
- Wagmi/Viem for wallet connection and blockchain interaction
- Vue Router for navigation
- Axios for GraphQL queries to Ponder indexer
- Sass-based component styling
Architecture
Chain Configuration
src/services/chainConfig.ts centralizes endpoint resolution. All composables accept chainId as a parameter and resolve endpoints via chainConfigService.getEndpoint(chainId, type). Supported chains: 31337 (Anvil), 84532 (Base Sepolia), 8453 (Base Mainnet).
Wagmi Integration
src/wagmi.ts manages wallet connection. Wagmi is the source of truth for which chain the wallet is on. Composables don't import wallet state directly — they accept chainId for testability.
Key Composables
useWallet()— Wallet connection, balance, account stateusePositions(chainId)— Loads staking positions from Ponder GraphQLuseStatCollection(chainId)— Protocol-wide statisticsuseSnatchSelection(demo, taxRateIndex, chainId)— Calculates snatchable positionsuseStake()— Executes staking transactionsuseAdjustTaxRate()— Tax rate adjustmentuseUnstake()— Position exit transactions
Key Components
StakeView.vue— Main staking dashboard (route:/dashboard)StakeHolder.vue— Staking form with accessibility-focused UIConnectWallet.vue— Wallet connection modalChartComplete.vue— Position visualizationCollapseActive.vue— Expandable position card with actions
viem v2 Contract Call Patterns
slot0()returns an array, not a record.tickis at index 1:slot0Response[1]. Do not accessslot0Response.tick— that was viem v1 behaviour.CheatsView.vuehandles both formats for backward compat.- When adding new contract reads, check whether viem v2 returns an array or named tuple before indexing into the result.
Development Workflow
- Boot full stack:
./scripts/dev.sh start(see root AGENTS.md) - Targeted:
npm run dev(assumes Ponder/Anvil already running) - Build:
npm run build - E2E tests:
npm run test:e2efrom repo root (Playwright)
Testing
- E2E tests in
tests/e2e/(repo root) cover complete user journeys - Uses mocked wallet provider with Anvil accounts
- Relies on semantic HTML and ARIA attributes for selectors
- StakeHolder test hooks:
page.getByRole('slider', { name: 'Token Amount' }),page.getByLabel('Tax')
Analytics
The web app uses @harb/analytics (self-hosted Umami wrapper) for funnel event tracking. initAnalytics(VITE_UMAMI_URL, VITE_UMAMI_WEBSITE_ID) is called in main.ts. Tracked events:
| Event | Where | Helper |
|---|---|---|
wallet_connect |
useWallet.ts — on first address set |
trackWalletConnect() |
swap_initiated |
useSwapKrk.ts — before buy/sell tx |
trackSwapInitiated('buy'|'sell') |
stake_created |
useStake.ts — after PositionCreated event |
trackStakeCreated() |
All calls are silent no-ops if Umami is unavailable. Env vars: VITE_UMAMI_URL, VITE_UMAMI_WEBSITE_ID.
Quality Guidelines
- Composables accept
chainIdparameter instead of importing wallet state directly - Each composable maintains its own
watchChainId()listener for independence - Always expose
xxxErrorandloadingrefs for UI feedback - Use strongly typed interfaces for Position, StatsRecord, etc.
- Use semantic HTML, ARIA attributes, and keyboard navigation
- Clear watchers and retry timers in
onUnmounted()