docs: consolidate and update all documentation for launch readiness
- Rewrite root README.md with proper project overview, tech stack, and repo structure - Remove duplicate CLAUDE.md files (root, onchain, ponder) — AGENTS.md is the standard - Update HARBERG.md to reflect Stage 1 completion and Stage 2 evolution - Delete stale onchain/testing_todos.md (all high-priority items completed) - Update VERSION_VALIDATION.md for VERSION=2 - Trim root AGENTS.md: replace Docker duplication with docs/docker.md reference - Trim onchain/AGENTS.md (129→71 lines): reference TECHNICAL_APPENDIX for formulas - Trim web-app/AGENTS.md (278→55 lines): remove internal API docs, keep architecture - Rewrite onchain/README.md: add contract table, deployment addresses, analysis links - Trim services/ponder/README.md: remove stale subgraph comparison - Add otterscan to docs/docker.md service topology - Update TECHNICAL_APPENDIX.md references Net: -388 lines across documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b7260b2eaf
commit
de3c8eef94
14 changed files with 249 additions and 701 deletions
|
|
@ -9,270 +9,46 @@ Vue 3 + TypeScript staking interface for KRAIKEN, enabling users to stake tokens
|
|||
- Axios for GraphQL queries to Ponder indexer
|
||||
- Sass-based component styling
|
||||
|
||||
## Architecture Overview
|
||||
## Architecture
|
||||
|
||||
### Chain Configuration Service
|
||||
|
||||
**Location**: `src/services/chainConfig.ts`
|
||||
|
||||
Centralizes all endpoint resolution for different blockchain networks. This service eliminates scattered configuration and provides a single source of truth for network-specific URLs.
|
||||
|
||||
**Key Method**:
|
||||
```typescript
|
||||
chainConfigService.getEndpoint(chainId: number, type: 'graphql' | 'rpc' | 'txnBot'): string
|
||||
```
|
||||
|
||||
**Supported Chains**:
|
||||
- `31337` - Local Anvil fork (development)
|
||||
- `84532` - Base Sepolia (testnet)
|
||||
- `8453` - Base Mainnet (production)
|
||||
|
||||
**Usage Pattern**:
|
||||
```typescript
|
||||
// Composables receive chainId and resolve endpoints internally
|
||||
const endpoint = chainConfigService.getEndpoint(chainId, 'graphql');
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Single source of truth for all endpoint configuration
|
||||
- Easy to test composables with different chainIds
|
||||
- No hidden global state dependencies
|
||||
- Clear error messages when endpoints aren't configured
|
||||
### 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.
|
||||
|
||||
**Configuration**: `src/wagmi.ts`
|
||||
### Key Composables
|
||||
- `useWallet()` — Wallet connection, balance, account state
|
||||
- `usePositions(chainId)` — Loads staking positions from Ponder GraphQL
|
||||
- `useStatCollection(chainId)` — Protocol-wide statistics
|
||||
- `useSnatchSelection(demo, taxRateIndex, chainId)` — Calculates snatchable positions
|
||||
- `useStake()` — Executes staking transactions
|
||||
- `useAdjustTaxRate()` — Tax rate adjustment
|
||||
- `useUnstake()` — Position exit transactions
|
||||
|
||||
Wagmi manages wallet connection and tracks the user's active blockchain:
|
||||
- Wallet providers: WalletConnect, Coinbase Wallet
|
||||
- Supported chains: Local fork (31337), Base Sepolia (84532)
|
||||
- State persistence via localStorage
|
||||
|
||||
**Chain Determination Flow**:
|
||||
1. Wagmi detects wallet's current chain via `watchChainId()`
|
||||
2. `useWallet` exposes `account.chainId` from wagmi state
|
||||
3. Components read `account.chainId` and pass to composables
|
||||
4. Composables watch for chain changes independently and reload data
|
||||
5. `ChainConfigService` maps chainId → endpoint URLs
|
||||
|
||||
**Key Insight**: Wagmi is the source of truth for *which chain the wallet is on*, but composables don't import wallet state directly. They accept `chainId` as a parameter for better testability and explicit dependencies.
|
||||
|
||||
## Key Composables
|
||||
|
||||
### `useWallet()`
|
||||
**Purpose**: Manages wallet connection, balance, and account state
|
||||
**Exports**: `balance`, `account`, `loadBalance()`
|
||||
**Watchers**:
|
||||
- `watchAccount()` - Reloads balance when account/chain changes
|
||||
- `watchChainId()` - Reloads balance on chain switch
|
||||
|
||||
**Note**: Also exports `chainData` computed property for UI metadata (Uniswap links, chain names). This is separate from endpoint resolution, which goes through `ChainConfigService`.
|
||||
|
||||
### `usePositions(chainId: number)`
|
||||
**Purpose**: Loads and manages staking positions from Ponder GraphQL
|
||||
**Parameters**: `chainId` - Which chain to query positions from
|
||||
**Exports**: `activePositions`, `myActivePositions`, `myClosedPositions`, `tresholdValue`, `positionsError`, `loading`
|
||||
**Key Features**:
|
||||
- Watches contract events (`PositionCreated`, `PositionRemoved`) to auto-refresh
|
||||
- Independent `watchChainId()` listener reloads on chain switch
|
||||
- Exponential backoff retry on GraphQL failures (1.5s → 60s max)
|
||||
|
||||
**Data Flow**:
|
||||
```
|
||||
Component (chainId) → usePositions(chainId)
|
||||
→ resolveGraphqlEndpoint(chainId)
|
||||
→ chainConfigService.getEndpoint(chainId, 'graphql')
|
||||
→ axios.post(endpoint, query)
|
||||
```
|
||||
|
||||
### `useStatCollection(chainId: number)`
|
||||
**Purpose**: Loads protocol-wide statistics (total supply, outstanding stake, inflation, etc.)
|
||||
**Parameters**: `chainId` - Which chain to query stats from
|
||||
**Exports**: `kraikenTotalSupply`, `stakeTotalSupply`, `outstandingStake`, `profit7d`, `inflation7d`, `maxSlots`, `claimedSlots`, `statsError`, `loading`
|
||||
**Retry Logic**: Same exponential backoff as `usePositions`
|
||||
|
||||
### `useSnatchSelection(demo, taxRateIndex, chainId)`
|
||||
**Purpose**: Calculates which positions can be "snatched" based on staking amount and tax rate
|
||||
**Parameters**:
|
||||
- `demo` - Include own positions in selection
|
||||
- `taxRateIndex` - Maximum tax rate index to snatch
|
||||
- `chainId` - Optional chain ID
|
||||
**Dependencies**: `usePositions`, `useStatCollection`, `useStake`
|
||||
**Exports**: `snatchablePositions`, `shortfallShares`, `floorTax`, `openPositionsAvailable`
|
||||
|
||||
### `useStake()`
|
||||
**Purpose**: Executes staking transactions (stake, snatch-and-stake)
|
||||
**Contract Interaction**: Calls `Stake.sol` via wagmi/viem
|
||||
**State Management**: Tracks transaction states (StakeAble, SignTransaction, Waiting)
|
||||
|
||||
### `useAdjustTaxRate()`
|
||||
**Purpose**: Provides tax rate options and handles tax rate adjustments
|
||||
**Key Data**: `taxRates` array with pre-calculated yearly/daily rates
|
||||
|
||||
### `useUnstake()`
|
||||
**Purpose**: Handles position exit transactions
|
||||
**Contract Interaction**: Calls `exitPosition()` on Stake contract
|
||||
|
||||
## Key Components
|
||||
|
||||
### `StakeView.vue`
|
||||
**Route**: `/dashboard`
|
||||
**Purpose**: Main staking dashboard showing chart, statistics, active positions
|
||||
**Key Features**:
|
||||
- Passes `initialChainId` to all composables for consistent data loading
|
||||
- Displays chain support status
|
||||
- Shows "Connect Wallet" prompt when disconnected
|
||||
|
||||
### `StakeHolder.vue`
|
||||
**Purpose**: Staking form with accessibility-focused UI
|
||||
**Key Elements**:
|
||||
- Token amount slider with ARIA labels
|
||||
- Tax rate selector dropdown
|
||||
- Real-time feedback (floor tax, positions buyout count)
|
||||
- Action button with state-driven labels (Stake / Snatch and Stake / Sign Transaction / Waiting)
|
||||
|
||||
**Accessibility**:
|
||||
- Semantic HTML with proper ARIA attributes
|
||||
- Screen reader announcements for dynamic content
|
||||
- Keyboard navigation support
|
||||
|
||||
**Test Hooks** (for Playwright):
|
||||
- `page.getByRole('slider', { name: 'Token Amount' })`
|
||||
- `page.getByLabel('Staking Amount')`
|
||||
- `page.getByLabel('Tax')`
|
||||
|
||||
### `ConnectWallet.vue`
|
||||
**Purpose**: Wallet connection modal with connector selection
|
||||
**Features**:
|
||||
- Shows connected wallet with avatar (blockies) and address
|
||||
- Token balance display ($KRK, Staked $KRK)
|
||||
- Buy button with chain-specific Uniswap link (uses `chainData.uniswap`)
|
||||
|
||||
### `ChartComplete.vue`
|
||||
**Purpose**: Position visualization showing tax rates and snatchable positions
|
||||
**Dependencies**: `usePositions`, `useStatCollection`, `useStake`
|
||||
|
||||
### `CollapseActive.vue`
|
||||
**Purpose**: Expandable position card with profit calculation and actions
|
||||
**Actions**: Pay Tax, Adjust Tax Rate, Unstake
|
||||
|
||||
## Shared Utilities
|
||||
|
||||
### `src/utils/logger.ts`
|
||||
Structured logging with namespaces (contract, info, error)
|
||||
|
||||
### `src/utils/helper.ts`
|
||||
Common helpers:
|
||||
- `bigInt2Number(value, decimals)` - Convert Wei to human-readable
|
||||
- `formatBigIntDivision(a, b)` - Safe BigInt division
|
||||
- `compactNumber(n)` - Format large numbers (1.5M, 3.2K)
|
||||
|
||||
### `src/config.ts`
|
||||
Chain configuration data:
|
||||
- `chainsData` - Array of chain metadata (id, graphql, contracts, etc.)
|
||||
- `getChain(id)` - Lookup chain by ID
|
||||
- `DEFAULT_CHAIN_ID` - Auto-detected based on environment/hostname
|
||||
|
||||
## Contract Interfaces
|
||||
|
||||
### `src/contracts/harb.ts`
|
||||
**Contract**: Kraiken.sol
|
||||
**Methods**: `getMinStake()`, `getAllowance()`, `getNonce()`, `approve()`
|
||||
**Setup**: `setHarbContract()` updates contract address when chain changes
|
||||
|
||||
### `src/contracts/stake.ts`
|
||||
**Contract**: Stake.sol
|
||||
**Methods**: `assetsToShares()`, `getTaxDue()`, `payTax(positionId)`
|
||||
**Setup**: `setStakeContract()` updates contract address when chain changes
|
||||
|
||||
## Retry Logic & Error Handling
|
||||
|
||||
Both `usePositions` and `useStatCollection` implement **exponential backoff retry** for GraphQL failures:
|
||||
|
||||
**Configuration**:
|
||||
- Base delay: 1.5 seconds
|
||||
- Max delay: 60 seconds
|
||||
- Backoff multiplier: 2x (1.5s → 3s → 6s → 12s → 24s → 48s → 60s)
|
||||
|
||||
**Why Retry**:
|
||||
- Ponder indexer may not be ready during stack startup
|
||||
- Temporary network issues shouldn't permanently break UI
|
||||
- Chain switching endpoints may have brief response delays
|
||||
|
||||
**Implementation** (duplicated in both files):
|
||||
- `formatGraphqlError()` - Parse Axios errors into user-friendly messages
|
||||
- `clearXxxRetryTimer()` - Cancel pending retry
|
||||
- `scheduleXxxRetry()` - Queue retry with exponential backoff
|
||||
- `resolveGraphqlEndpoint()` - Resolve chainId to GraphQL URL
|
||||
|
||||
**Refactoring Opportunity**: ~41 lines of duplicate code could be extracted to `src/utils/graphqlRetry.ts`
|
||||
### Key Components
|
||||
- `StakeView.vue` — Main staking dashboard (route: `/dashboard`)
|
||||
- `StakeHolder.vue` — Staking form with accessibility-focused UI
|
||||
- `ConnectWallet.vue` — Wallet connection modal
|
||||
- `ChartComplete.vue` — Position visualization
|
||||
- `CollapseActive.vue` — Expandable position card with actions
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Running Locally
|
||||
Boot the full stack with `./scripts/dev.sh start` (see root `CLAUDE.md` for details)
|
||||
|
||||
### Targeted Development
|
||||
- `npm run dev` - Vite dev server (assumes Ponder/Anvil already running)
|
||||
- `npm run build` - Production build with type checking
|
||||
- `npm run preview` - Preview production build
|
||||
- `npm run test:e2e` - Playwright E2E tests (from repo root)
|
||||
|
||||
### Live Reload
|
||||
Use `./scripts/watch-kraiken-lib.sh` to rebuild `kraiken-lib` on file changes and auto-restart dependent containers
|
||||
- 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:e2e` from repo root (Playwright)
|
||||
|
||||
## Testing
|
||||
|
||||
### E2E Tests
|
||||
**Location**: `tests/e2e/` (repo root)
|
||||
**Framework**: Playwright
|
||||
**Coverage**: Complete user journeys (mint ETH → swap KRK → stake)
|
||||
**CI**: Woodpecker e2e pipeline runs these against pre-built service images
|
||||
|
||||
**Test Strategy**:
|
||||
- Use mocked wallet provider with Anvil accounts
|
||||
- Tests automatically start/stop the full stack
|
||||
- Rely on semantic HTML and ARIA attributes (not private selectors)
|
||||
|
||||
### Manual Testing Checklist
|
||||
1. Connect wallet (WalletConnect / Coinbase Wallet)
|
||||
2. Switch chains and verify data reloads
|
||||
3. Stake tokens with different tax rates
|
||||
4. Snatch positions with higher tax rate
|
||||
5. Adjust tax rate on existing position
|
||||
6. Pay tax manually
|
||||
7. Unstake position
|
||||
8. Verify GraphQL error retry behavior (kill Ponder, observe retries)
|
||||
- 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')`
|
||||
|
||||
## Quality Guidelines
|
||||
|
||||
- **Composables**: Accept `chainId` parameter instead of importing wallet state directly
|
||||
- **Watchers**: Each composable maintains its own `watchChainId()` listener for independence
|
||||
- **Error States**: Always expose `xxxError` and `loading` refs for UI feedback
|
||||
- **Type Safety**: Use strongly typed interfaces for Position, StatsRecord, etc.
|
||||
- **Accessibility**: Use semantic HTML, ARIA attributes, and keyboard navigation
|
||||
- **Testability**: Design components to work with Playwright role/label selectors
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Lazy load routes with Vue Router dynamic imports
|
||||
- Debounce `assetsToShares()` calls in StakeHolder (500ms)
|
||||
- Use `computed()` for derived state to avoid recalculation
|
||||
- Clear watchers in `onUnmounted()` to prevent memory leaks
|
||||
- Cancel retry timers on unmount or manual actions
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Don't** import `chainData` from `useWallet` in composables - pass `chainId` explicitly
|
||||
2. **Don't** forget to pass `chainId` to `loadPositions()` / `loadStats()` on manual refresh
|
||||
3. **Don't** call composables conditionally - they must run on every component render
|
||||
4. **Don't** forget to clear timers/watchers in `onUnmounted()`
|
||||
5. **Do** reset retry delays on successful loads (`retryDelayMs.value = BASE_DELAY`)
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Extract retry logic to shared utility (`src/utils/graphqlRetry.ts`)
|
||||
- Remove dead code in `useChain.ts` (entire file is commented out)
|
||||
- Clean up commented debug `console.log()` statements
|
||||
- Implement three-way version validation (contract VERSION ↔ Ponder ↔ web-app)
|
||||
- Add GraphQL query caching layer for better performance
|
||||
- Composables accept `chainId` parameter instead of importing wallet state directly
|
||||
- Each composable maintains its own `watchChainId()` listener for independence
|
||||
- Always expose `xxxError` and `loading` refs 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()`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue