Appearance
ApplicationManager
Manages the Trade Fund application lifecycle from creation through approval or rejection.
High-Level Design
Application State Machine
pending ──► pending-interview ──► approved
│ │ ▲
├────────┬───────┘ │
│ ▼ │
│ rejected │
└─────────────────────────────────────┘An application is created when a user passes a paper trading challenge (triggered via RPC from ChallengeManager in UserPaperTradePortfolio). It enters the pending state and sits in an admin review queue. From there, an admin can approve it directly, reject it, or schedule an interview first.
Creation-Time Stats Snapshot
At creation, the manager captures a frozen snapshot of the user's challenge performance:
realizedPnl,consistencyPercentage,tradingDayseligibility("eligible" / "contender" / "N/A") — computed by the caller viaAdminReviewManager.calculateTradeFundEligibility()before creationmllRisk(boolean) — whether the user came close to the max loss limitwalletAddress
This snapshot is immutable — it reflects the user's stats at the moment the challenge was passed, not at review time.
Split Approval Responsibility
Approval is not handled by ApplicationManager. The parent DO (TradeFundAccountRegistry.approveApplication()) orchestrates the multi-step process:
- Validates the application status is
pendingorpending-interview MasterAccountManager.createAndAssignMasterAccount()— provisions the funded Hyperliquid account + agent walletApplicationManager.markAsApproved()— simple status update (no validation, trusts parent)MonitoringManager.ensureAlarmScheduled()— starts MLL monitoring for the new account
Rejection, by contrast, is handled directly by ApplicationManager since it requires no cross-manager coordination.
One Pending Application Per User
Enforced at creation via SQL check: if a user already has an application with status pending or pending-interview, the new creation is rejected. This prevents duplicate applications from concurrent challenge completions.
Edge Cases & Error Handling
createTradeFundApplicationwraps all logic in try/catch, returningRPCResultwithsuccess: falseon any error.markAsApproved(),rejectTradeFundApplication(), andscheduleInterview()have no status guards — they trust the caller to validate status before calling. Any status can be transitioned.- Stats fields are optional for backwards compatibility; expected for non-FREE plans.
mllRiskstored as integer (0/1) in SQLite, mapped to boolean in TypeScript.
See Also
- AdminReviewManager - Enhanced listing, remarks, audit logs, bulk operations.
- MasterAccountManager - Creates funded accounts on approval.
- Parent DO:
TradeFundAccountRegistry