Technical Architecture
Knox Protocol's smart contract system is built on a modular, upgradeable architecture using EIP-1167 minimal proxies for gas-efficient pool deployment.
System Overview
SpectrumFactory
│
├── SpectrumAccountant
├── AccessController
│
├── TrancheVault
├── IAllocator (Senior / Spectrum / Junior)
│
└── UnderlyingMarketCore Contracts
KnoxRouter
Purpose: User-facing entry point for all deposits
Key Functions:
depositSeniorSpectrum(accountant, assets, receiver)depositSpectrumTranche(accountant, apyBps, assets, receiver)depositJuniorSpectrum(accountant, assets, receiver)
Permit2 variants:
depositSeniorSpectrumPermit2(...)etc.
Flow:
Pull assets from user (via
transferFromor Permit2)Approve accountant to spend assets
Call corresponding accountant deposit function
Reset approval to
0(defensive cleanup)
Why Router?
Single contract interface for users
Handles approval management
Supports both ERC20 approvals and Permit2 signatures
Cleaner UX than direct accountant calls
SpectrumAccountant
Purpose: The brain of each pool
Responsibilities:
Deposit validation and processing
Share pricing calculations
Lazy spectrum vault creation
Waterfall computation
Settlement execution
Async redeem tracking
Rescue mechanism
Key Functions:
Deposits
depositSenior(assets, receiver)— validates capacity, snapshots yield, mints sharesdepositSpectrum(apyBps, assets, receiver)— validates grid, creates vault if new, mints sharesdepositJunior(assets, receiver)— computes residual value, mints sharesregisterDeposit(amount)— callback from vault direct deposits
Lifecycle
redeemShares(shares)— exit underlying market (sync or async)claimRedeemedAssets()— claim async exits (permissionless)settlePool()— execute waterfall, transition toSETTLED
Views
seniorTrancheCurrentValue()— compounded senior valuejuniorTrancheCurrentValue()— residual after waterfallseniorCapacity()— max senior deposits allowedunderlyingAssetCurrentValue()— current market valuemaxPendingExitUnlockAt()— latest async exit timestamp
Deployment: Cloned via SpectrumFactory.deploy() (EIP-1167 minimal proxy)
TrancheVault
Purpose: ERC4626 vault for each tranche position
Instances per pool:
1 ×Senior vault (created at deployment)1 ×Junior vault (created at deployment)N ×Spectrum vaults (created lazily)
Key Modifications from Standard ERC4626:
deposit()— blocked after pool maturitywithdraw()— blocked until pool isSETTLEDShare pricing delegated to accountant via callbacks
accountantMint()— function for accountant-driven minting
Share Pricing:
Calls accountant.previewDeposit(vault, assets) which dispatches based on vault type:
For senior/spectrum: compounding formula
For junior: proportional to current waterfall value
Special Behavior:
Last redeemer gets entire remaining balance (prevents dust)
Deployment:
Senior/junior: cloned by factory during pool deployment
Spectrum: cloned by accountant on first deposit at new grid rate
SpectrumFactory
Purpose: Deploy new pools atomically
Single-Transaction Deployment:
What Gets Created:
SpectrumAccountantclone (EIP-1167)Senior
TrancheVaultcloneJunior
TrancheVaultclone
All initialized in the same transaction.
Pool immediately transitions to ACTIVE.
Access Control:
DEPLOYER_ROLE— can calldeploy()FEE_SETTER_ROLE— can toggleapplyProtocolFeesRESCUE_ROLE— delegated to accountants for rescue calls (resolved dynamically fromAccessController)
Global State:
applyProtocolFees— global on/off switch for protocol fee collection
AccessController
Purpose: Centralized role management
Based on: OpenZeppelin AccessControl
Roles:
DEFAULT_ADMIN_ROLE— can grant/revoke all rolesDEPLOYER_ROLE— authorized to deploy poolsFEE_SETTER_ROLE— toggle protocol feesRESCUE_ROLE— execute rescue calls on accountantsCLAIM_TARGET_ADMIN_ROLE— register claim targets (Strata allocator)
Dynamic Resolution: Accountants query the factory's AccessController at call time, allowing role rotation post-deployment.
Allocators (IAllocator Interface)
Purpose: Pluggable adapters between accountant and yield markets
Interface Methods
Implementations
ERC4626Allocator
Markets: Standard ERC4626 vaults (e.g., Morpho Blue) Redemption: Synchronous
Flow:
deposit()→vault.deposit()redeem()→vault.redeem()
AaveV3L2Allocator
Markets: Aave V3 on L2 chains Redemption: Synchronous Optimization: Calldata compression for L2 gas savings
Flow:
deposit()→pool.supply()redeem()→pool.withdraw()
ERC4626AsyncStrataMainnetAllocator
Markets: Strata Finance (Ethena USDe, Neutrl NUSD on mainnet) Redemption: Asynchronous (7-day cooldown)
Flow:
deposit()→vault.deposit()requestRedeem()→vault.requestRedeem()+store cooldown in exit recordclaimRedeem()→ checkIUnstakeCooldown.activeRequests()+finalize()
Exit Handle Format:
Claim Target Registration:
CLAIM_TARGET_ADMIN_ROLE can register new unstake contracts for new asset types.
Deposit Flow Details
Senior Value Tracking (Snapshot Mechanism)
Problem: Deposits arrive at different times, each earning from their deposit timestamp to maturity.
Solution: Piecewise compounding via snapshots
State Variables:
yieldLastSeniorDeposit— accumulated yield at last deposittimeLastSeniorDeposit— timestamp of that snapshot
Before Each Senior Deposit:
Computing Current Value:
Effect: Each deposit's accumulated value becomes the new base principal for subsequent compounding.
Example:
Day 0: Deposit $100 → 105 shares (5% APY, 365d)
Day 183: Senior value = $102.47
Snapshot:
yieldLastSeniorDeposit = $2.47
New deposit $50 → shares =
New principal:
$150 + $2.47 = $152.47This
$152.47now compounds for remaining 182 days
Lazy Spectrum Vault Creation
When: First deposit at a new grid rate
Validate grid alignment
Check if vault exists
Clone vault implementation (EIP-1167)
Initialize vault
Register vault
Insert rate into sorted array (O(n) insertion sort)
Proceed with deposit
activeSpectrumRates Array: Maintained in sorted order (low → high) for O(n) waterfall iteration
Rescue Mechanism
Availability: tStart + dPeriod + 28 days after maturity
Purpose: Recover stuck assets from edge cases
Safety Rails:
Role resolved dynamically from factory (allows rotation)
Asset transfers restricted to tranche vaults or accountant
Unrestricted for other targets (allocators, markets)
Key Takeaways
Modular design: Router → Accountant → Allocator → Market
Gas-efficient: EIP-1167 clones for pool deployment
Lazy creation: Spectrum vaults created on-demand
Flexible allocators: Pluggable adapters for any yield source
Snapshot tracking: Piecewise compounding for senior value
Safety mechanisms: Dynamic roles, rescue with restrictions
Async support: Native handling of cooldown-based markets