Core Concepts
🚧 In Development
This page reflects v3.0 core concepts. The binary AS_POOL/FUND_POOL model is replaced by a 24-dimension config model. See Pool Models for the full dimension list.
Foundational models and mechanisms that define how Aset works.
🏊 Config-Driven Pools (v3.0)
Pools are no longer split by type (AS_POOL/FUND_POOL). Each pool is defined by ~24 independent dimensions: fund_wallet, reserve_bps, maturity_model, kyc_level_required, tranche_group_id/tranche_priority (for tranched products, v3-14), and so on. Same Pool contract, same flow — only the configuration changes. NAV is always computed by Aset oracle Lambda (v3-15).
How it works
Deposit flow: investor → pool contract → reserve share to reserve_wallet, remainder to fund_wallet; LP is minted to the investor.
🔀 LP Issuance (Always Platform-Issued)
Aset always mints the LP token via PlatformLPToken. One LP contract per pool — single source of truth for investor positions. FUND_ISSUED mode is deprecated; partners no longer mint their own LP.
For tranched products (v3-14), 2-3 separate pools are deployed sharing a tranche_group_id — each is a standard SINGLE pool with its own LP token. No two-LP-per-pool pattern.
💰 NAV Token Pricing
LP token price floats down from $1.00 (capped at $1.00 max, no premium pricing). The lower bound is 1e-6, not $0.00 — zero is not representable, so a total loss prices at 1e-6 and is communicated by the lifecycle move to IMPAIRED, not by the number (Writedown & NAV). Token price IS the loss indicator — no separate PF multiplier. New investors pay the current NAV (fair entry). Investment always stays open — never auto-blocked. tokens_minted = deposit / nav_per_token.
How it works
Example: a $10,000 deposit with reserve_bps = 1000 sends $1,000 to reserve_wallet and $9,000 to fund_wallet. Reserve is not a loss-absorption layer; using external funds for redemptions requires separate funding into the pool.
See Writedown & NAV for the full formula.
🔮 NAV Data Sources & Oracle (v3.0)
All pools use Aset oracle Lambda to compute NAV (v3-15). Raw inputs come from partner API (Joob eNote, LINE BK risk dashboard, etc.) or admin form; Aset applies its formula — incl. write-off schedule (v3-13) and tranche waterfall (v3-14). There is no "partner-reported NAV" path that skips Aset processing.
In both cases, Aset is the on-chain caller of updateNAV() (oracle role). NAV decreases enter a 24h timelock; increases apply immediately.
How it works
Oracle service (Aset-operated) calls PlatformPool.updateNAV(new_nav). Row created in nav_history with status PENDING. After 24h timelock expires (for decreases), status → APPLIED. Admin can cancel during the timelock window → status CANCELLED.
🛡️ Reserve Fund — redemption liquidity, not first-loss
In the current external-reserve implementation, the reserve_bps share of each deposit goes to reserve_wallet and the remainder goes to fund_wallet. The in-pool reserve counter and getter are removed. The ratio specifies routing, not retention. An external balance does not automatically fund a pool payout.
It does not absorb loss (R8). Because it is carved out of investor deposits, it is already inside the claim NAV prices — netting it against a loss would credit investors twice for their own money. Order of loss absorption: collateral → buffer → Junior → Mezzanine → Senior → NAV, with no reserve step. Reserve is also separate from tranche groups (v3-14).
How it works
Example: a $10,000 deposit with reserve_bps = 1000 sends $1,000 to reserve_wallet and $9,000 to fund_wallet. Reserve is not a loss-absorption layer; using external funds for redemptions requires separate funding into the pool.
⚠️ At launch reserve_bps is 0 by design, so on a standalone pool the buffer is the only cushion between a loss and NAV — which is why R6 turns it from a disclosure sentence into a real parameter.
📈 Yield Model
Claim-based only. Partner deposits yield USDC into Pool via Pool.depositYield(gross). Aset Lambda computes fees off-chain and calls Pool.settleYield(stablecoin, net, treasuryFee, poolMgmtFee) — one tx that credits net to LP holders and pays both fee legs. Yield is tagged per investor (pro-rata by LP holdings). Investor calls claimYield() to receive USDC, or reinvest(stablecoin, amount) for re-deposit.
Settings: yield_frequency, allow_rollover, min_reinvest_amount. (yield_trigger removed v3-20 — claim/manual only.) ⚠️ Reinvest is not offered on any screen in MVP (2026-08-27, not yet reflected, v3-151) — the investor CTA above and the allow_rollover toggle both come off. The on-chain reinvest() and POST /yield/reinvest stay; what closes the path is allow_rollover's false default. → 05 → Reinvest V1 Policy
Partial reinvest is allowed — any amount up to the claimable balance, subject to min_reinvest_amount; only an over-request reverts (05 → Reinvest V1 Policy). Same-pool only by design — reinvest() spends that pool's accrued yield and mints that same pool's LP; there is no cross-pool reinvest (to allocate yield elsewhere, claimYield() to wallet then deposit() into the other pool). See v3-64.
🎫 No Receipt NFT (v3.0)
Deposit is atomic in v3.0: USDC + LP mint + fund_wallet split happen in one transaction. The LP token itself is proof of deposit. PlatformReceiptNFT contract is removed.
Legacy receipt records remain in the database for historical Fund Pools, but no new receipts are minted.
🪪 KYC / Soulbound Token
SumSub integration for KYC/KYB. On approval, a non-transferable ERC-721 (PlatformKYCSoulbound) is minted on-chain. Required before any investment.
Per-pool gating via kyc_level_required (KYC for individuals, KYB for institutions) and jurisdiction_whitelist (ISO country codes). Empty whitelist = all jurisdictions allowed.
See KYC & Identity.
📊 Risk Transparency
No numeric risk rating (1–5) — false precision + legal liability. Instead: transparent data points. Risk signals: collateral_description, collateral_ratio, category, reserve_bps, nav_per_token, apy_rate, apy_disclosure, lockup_days, penalty_type, tranche_role (v3-14), pool age. Investors assess risk themselves.
risk_tier enum and collateral_type enum are deprecated. collateral_type is replaced by free-text collateral_description + optional collateral_ratio.
🏦 Non-Custodial Architecture
All investor funds flow through Pool Smart Contracts. Aset has an operator role (can call functions like pause(), updateNAV()) but never holds investor funds. No VASP license required (€150K MiCA capital threshold avoided).
Where funds live:
- Deposit → Pool Contract (atomic split into reserve + fund_wallet)
- Reserve → Pool Contract (10% by default)
- Fund capital (the partner remainder,
amount − reserve) →fund_wallet(partner-controlled — never Aset treasury; v3-26 every pool is fund-linked) - Yield (pre-claim) → Pool Contract
- Yield (post-claim) → Investor wallet
- Redemption → Pool Contract → Investor
⚠️ Wind-Down Recovery Limit
If a partner becomes unresponsive, only what the Pool contract already holds is recoverable on-chain — typically just the reserve (~10%), plus any capital previously recalled from the partner (totalEpochTopUp), which an unresponsive partner is unlikely to have added. The partner remainder in fund_wallet is partner-controlled — recovering it requires off-chain legal action. See Pool Models → Emergency Wind-Down.
🚫 KYC Rejection Criteria
SumSub auto-decides based on rejection reason code → mapped to RETRY or FINAL. RETRY: blurry photo, glare, cropped ID, expired doc, name mismatch — no limit on retries. FINAL: sanctioned country (OFAC/EU), fraud detected, underage, duplicate applicant — admin cannot override.
Removed in v3.0
pool_typeenum (AS_POOL/FUND_POOL) — replaced by dimensionsescrow_modelenum — always SMART_CONTRACTyield_distribution_modelenum — always MANUAL_CLAIMlp_issuance_modelenum — always PLATFORM_ISSUEDPlatformReceiptNFTcontract — LP is proof of deposit- D+7 refund mechanism — deposits are atomic
collateral_typeenum — replaced bycollateral_description+collateral_ratio