Appearance
TradeFundAccountRegistry
Global singleton DO (id: "global") that manages the full lifecycle of funded trading accounts — from application intake through account provisioning, live monitoring, and payouts.
High-Level Design
Core Responsibility
Bridges the gap between paper trading challenges and real funded accounts on Hyperliquid. When a user passes a challenge, this DO receives the application, provisions a real trading account via AWS KMS, monitors it for risk, and handles profit payouts through a two-multisig Gnosis Safe flow.
Managers
| Manager | Role |
|---|---|
| ApplicationManager | Application queue CRUD with status tracking (pending → pending-interview → approved | rejected). Prevents duplicate pending applications per user. |
| MasterAccountManager | Heaviest manager. Handles KMS key creation, Hyperliquid account setup, agent key lifecycle, health checks, and account deactivation with fund recycling. |
| MonitoringManager | 30-second alarm loop that checks all active accounts for MLL breaches, unlisted token violations, and tracks winning days at UTC day boundaries. |
| PayoutManager | Payout request lifecycle with eligibility validation, fund transfer to receiver multisig, and Gnosis Safe transaction creation for user disbursement. |
| AdminReviewManager | Bulk approve/reject (up to 50), cursor-based pagination, append-only remarks system, and audit logging with old/new value tracking. |
Data Flow
Challenge passed (UserPaperTradePortfolio DO)
→ ApplicationManager.createTradeFundApplication() [status: pending]
→ Admin reviews / schedules interview [status: pending-interview]
→ DO.approveApplication() [orchestration point]
├─ MasterAccountManager.createAndAssignMasterAccount()
│ 1. Create AWS KMS key (secp256k1)
│ 2. Insert master account + monitoring + params records
│ 3. Accept Hyperliquid terms (KMS-signed)
│ 4. Deposit capital from depositor wallet
│ 5. Create + authorize agent account
│ 6. Status → ACTIVE
│ (intermediate statuses: CREATED → ACCEPTED_TERMS → DEPOSITED → AUTHORIZED → ACTIVE)
├─ ApplicationManager.markAsApproved()
└─ MonitoringManager.ensureAlarmScheduled() [starts 30s loop]
Monitoring alarm (every 30s)
→ For each active account:
→ MasterAccountManager.checkAccountHealth()
│ Fetch HL clearinghouse state → check MLL + unlisted tokens
→ If breached: deactivateMasterAccount()
│ Close positions → cancel orders → revoke agents → recycle funds
→ At UTC day boundary: track winning days
Payout request (user-initiated)
→ PayoutManager.createPayoutRequest()
→ Validate: active account, winning days met, withdrawable - safetyNet ≥ amount
→ usdSend from master account → receiver multisig (HL L1)
→ Admin creates Gnosis Safe tx → sender multisig (Arb L2) → user walletKey Concepts
KMS-Only Signing — Master account private keys never exist in memory. All Hyperliquid operations (accept terms, authorize agents, send USD, close positions) are delegated to AWS KMS via a KmsSigner → viem LocalAccount adapter.
Agent Accounts — Delegated trading keys that let the user's terminal trade on behalf of the master account. Generated locally, authorized on Hyperliquid (KMS-signed), then encrypted at rest with AES-256-GCM (key derived via HKDF from KMS master secret). 180-day expiry with manual rotation. Two types: USER (trading terminal) and ADMIN (operations).
MLL (Maximum Loss Limit) — initialCapital × (1 - mllRatio). If account value hits this threshold, the account is immediately deactivated. Breach data is recorded idempotently (first breach preserved).
Safety Net — Protected buffer that cannot be withdrawn. Available payout = withdrawable - safetyNet (where withdrawable is the Hyperliquid withdrawable balance).
Payout Split — Configurable user/platform ratio per plan (e.g., 80/20 standard, 90/10 meme plans).
Two-Multisig Payout Architecture — Funds flow through two separate multisigs: master account →usdSend on HL L1→ receiver multisig, then sender multisig →ERC20 USDC on Arbitrum L2→ user's payout address. This separates Hyperliquid-side operations from Ethereum-side Gnosis Safe execution.
Winning Days — Tracked at UTC day boundaries during the monitoring alarm. A day counts as "winning" if profit ≥ targetProfitWinningDay. Users must accumulate enough winning days before becoming payout-eligible.
Payout Params by Plan
| Plan | Capital | MLL Ratio | Safety Net | User Ratio | Min Winning Days |
|---|---|---|---|---|---|
| STARTER | $1,000 | 10% | $300 | 80% | 5 |
| STANDARD | $10,000 | 6% | $800 | 80% | 5 |
| PRO | $20,000 | 6% | $1,400 | 80% | 5 |
| STARTER_MEME | $1,000 | 10% | $300 | 80% | 5 |
| STANDARD_MEME | $10,000 | 6% | $800 | 90% | 3 |
| PRO_MEME | $20,000 | 6% | $1,400 | 90% | 3 |
All plans share: minPayoutAmount = $100, targetProfitWinningDay = $100.