Skip to content

Overview & Architecture

🚧 Largely Built — Deploy/QA Pending

This page describes the v3.0 architecture. Contracts, DB schema, backend Lambdas, and both frontends are largely built; remaining work is deploy/QA (see Migration Status). v2.x architecture (AS_POOL/FUND_POOL binary model) is deprecated.

Aset is an institutional-grade RWA tokenization platform. Investors deposit stablecoins into pools backed by real-world assets (invoice financing, earned wage access, private credit) and earn yield through LP tokens.

🏦 Non-Custodial Architecture

All investor funds flow through Pool Smart Contracts. Aset operates as a facilitator with an operator role on contracts — it never holds investor funds directly. This means no VASP license is required under MiCA (€150K capital threshold avoided). Industry standard: Ondo, Centrifuge, Maple all use this model.

v3.0 Model — Config-Driven Pools

In v2.x, pools came in two binary types: AS_POOL (Aset-managed) and FUND_POOL (FM-managed). As we onboard more partners (Joob, LINE BK, future originators) with different operational models, we moved to a dimension-based configuration: each pool is defined by ~24 independent settings — not by a fixed type.

Key principles:

  • Non-custodial: Aset never holds funds, only the contract code can move them
  • Aset always mints LP: One LP token contract is the single source of truth for investor positions across all pools (partners notified via API)
  • Reserve and Tranche are separate: Reserve (liquidity buffer) and Junior tranche (loss absorber) serve different purposes. The reserve is not a loss layer — first-loss is the manager equity buffer for standalone pools, Junior for grouped ones (R8)

See Pool Models for the full dimension list.

v3.0 Highlights — What Changed from v2.x

📋 Quick reference for v3.0 design decisions

All 10 decisions are recorded in Decisions → v3.0 Decisions. Summary below.

#ChangeRationaleDetail
v3-01pool_type binary → ~24 orthogonal dimensionsJoob/LINE BK onboarding exposed binary as too rigidPool Models
v3-02LP always platform-issued (FUND_ISSUED deprecated)Single source of truth for positions across all poolsCore Concepts
v3-03PlatformReceiptNFT removed; deposits atomicLP itself is proof of deposit; no D+7 refund windowSmart Contracts
v3-04Redemption: PENDING_RESERVE replaces FM_ACCEPTEDPartner action is state, not workflow stepStatus Machines
v3-05Two-level pause: is_paused (soft) + is_emergency_frozen (hard)Soft pause blocks deposits only; freeze halts everythingPool Models → Operations
v3-06Emergency wind-down (60+30 day timelock)Recovery path for unresponsive partners. Only what the Pool contract holds is recoverable on-chain (typically the ~10% reserve)Pool Models → Wind-Down
v3-07collateral_type enum → collateral_description text + collateral_ratioReal collateral arrangements don't fit 3 bucketsCore Concepts
v3-08nav_data_source dimensionSuperseded by v3-15 — single processing path, dimension removed[v3-15 below]
v3-09tranche_structure dimensionSuperseded by v3-14 — tranching modeled via linked SINGLE pools, not two-LP-per-pool[v3-14 below]
v3-10Per-pool KYC: kyc_level_required + jurisdiction_whitelistKYC/KYB requirements differ per pool (institutional, region-restricted)KYC & Identity
v3-11PlatformEscrow absorbed into PlatformPool (5 → 4 contracts)10/90 split inlined; no separation-of-concerns benefit with atomic depositSmart Contracts
v3-12Wind-down unified with NAV mechanism; IMPAIRED state addedMath equivalent; Maple-pattern intermediate state lets us pause deposits without committing to terminal wind-downStatus Machines, Writedown & NAV
v3-13Automated NAV writedown via Joob DPD API (OJK schedule)Joob already exposes per-loan DPD; OJK 5/15/50/100% schedule applied per-loan, aggregated to NAVWritedown & NAV, Joob Pool Config
v3-14Tranche via linked SINGLE pools (supersedes v3-09) — tranche_group_id + tranche_role columns; Aset Lambda applies waterfallSingle-pool-two-LP pattern adds contract complexity without buying enforcement (Aset Lambda controls NAV anyway under v3-15). Two-pool pattern simpler, supports 3-tier (Senior/Mezz/Junior)Pool Models → Tranche Group
v3-15Drop PARTNER_REPORTED (supersedes v3-08) — Aset always processes NAVRequired to enforce tranche waterfall (v3-14) and write-off schedule (v3-13). Pure mirroring would break those guaranteesWritedown & NAV
v3-16Reserve consumption integrated into updateNAV(newNav, reserveConsumed) — atomicSingle Lambda call; race-free; cleaner audit trail vs. separate consumeReserveForLoss(). ⚠️ The "loss absorption" half is retired by R8 — the reserve is redemption liquidity, and reserveConsumed is now always 0. The atomicity rationale and the ABI standSmart Contracts, Writedown & NAV
v3-17Cross-VASP KYC: SumSub Reusable KYC if partner uses SumSub, else fast re-KYC with returning-user flowTokocrypto-class secondary market partnerships need low-friction onboarding without sacrificing regulatory complianceKYC & Identity
v3-18Minimum on-chain config + KYC compliance enforced on-chainPermissioned platform — investors trust Aset operationally. Only KYC level/jurisdiction must be on-chain (regulatory hard line). All other config in DBSmart Contracts

🔄 Phase 2.5 — Universal Holder Verification (5-State Model)

On top of v3.0, the 5-State Holder Model (A: Verified Investor, B: Verified No-History, C: Unverified Holder, D: Identity Only, E: Unregistered) unifies read-only / actual-investment / secondary-market scenarios into one state machine. Investors transition between states via SBT minting, RPS transfers, or platform deposits. → Holder Verification

Also unified in v3.0

  • Yield model: All pools use claim-based, manual-trigger (yield_trigger = MANUAL) — auto-distribution removed
  • Escrow: Always SMART_CONTRACT (legacy MULTI_SIG / NO_ESCROW removed)
  • Per-pool reserve: reserve_bps (default 1000 bps = 10%) — distinct from Junior tranche
  • Risk transparency: risk_tier removed; replaced by data points (collateral_*, apy_disclosure, tranche_structure, etc.)

Tech Stack

LayerStackDetails
🖥️ FrontendVite + React + TypeScriptReact Router · Tailwind 4 · RainbowKit · shadcn/ui
⚡ BackendAWS CDK + Lambda (Node.js)API Gateway v2 · CloudFront · Supabase JS
📜 Smart ContractsFoundry + SolidityOpenZeppelin · 4 contracts (PlatformReceiptNFT + PlatformEscrow removed) · Multi-chain
🗄️ DatabaseSupabase (PostgreSQL + RLS)Hosted · Row-level security · supabase-js
🔗 ChainsEthereum + Base + KaiaSIWE auth · wagmi + viem
👤 Auth (Investor)SIWE (Sign-In with Ethereum)JWT via jose · RainbowKit · 30-min timeout
🔑 Auth (Admin)Google OAuth + Multi-sigadmin_users.email from Google · multi-sig wallet for critical admin actions

Chain Deployment

v3.0 uses multi-chain deployment to support partners on different networks.

EnvironmentChainsNote
🧪 DevBase Sepolia, Ethereum SepoliaTestnets for development and staging
🌐 ProdEthereum Mainnet (Joob partner), Base Mainnet (default), Kaia (existing Joob holdings)Per-pool chain_id configuration

Each pool is deployed on the chain specified by its chain_id dimension. Stablecoin addresses are resolved per chain via STABLECOIN_ADDRESSES[chainId][symbol].

Smart Contract Registry (v3.0)

4 active contracts (down from 6 in v2.x — Receipt NFT removed v3-03, Escrow absorbed v3-11).

ContractStandardPurpose
PlatformPoolAccessControl + ReentrancyGuardCore contract — deposits, reserve management, LP mint, yield distribution, redemption, NAV updates, wind-down. Same flow for all pools (config-driven).
PlatformLPTokenERC-20 + AccessControlLP token — permissionless secondary transfers (pause-gated only; verification enforced at deposit/redemption, not on transfer — v3-58), auto yield settlement. One per pool always (v3-14: tranched products use multiple linked pools, each with their own LP, not two-LP-per-pool).
PlatformPoolFactoryFactory + RegistryUnified factory + on-chain registry. createPool(config) deploys Pool + LPToken(s). Single poolCounter. Validates dimensions at deploy.
PlatformKYCSoulboundNon-transferable ERC-721On-chain KYC/KYB status via SumSub. Global contract (not per-pool). Supports INDIVIDUAL/INSTITUTION levels, US person attestation, expiry, revocation.
PlatformEscrowRemoved in v3.0 (v3-11). Absorbed into PlatformPool — USDC held directly by the Pool; the 10/90 reserve split is inlined into the atomic deposit.
PlatformReceiptNFTRemoved in v3.0. LP token itself is proof of deposit.

See Smart Contracts for detailed function lists, roles, and architecture diagrams.

Migration Status

v3.0 design is complete; implementation is largely built. Remaining work is primarily deploy + QA, not spec.

ComponentStatus
Spec (docs site)✅ Current (verified 2026-08-06)
DB schema (new dim columns)✅ Built — through migration 0126 (applied to dev; prod deploy pending). Counts and per-migration detail: 11-db-schema
Smart contracts (PlatformReceiptNFT removal, new functions)✅ Built — matches this spec
Backend Lambdas (dimension-based dispatching)✅ Built — ~128 handlers / 129 routes / 15 schedulers
Frontend (pool creation UI uses dimensions)✅ Built — investor + admin apps (a few admin create-form inputs still API-only, QA pending)

Known deferred: automated DPD→OJK NAV writedown (v3-13) — see Writedown & NAV and Joob Pool Config.

Track implementation in the Phase 1.5 plan (internal).