Skip to main content
Trust boundaries and authorization across governance, controller, and pool: what crosses each edge and what gets checked there. Validate once at the boundary, trust after. Caller auth, oracle responses, and balance deltas are checked at entry; downstream code relies on that guarantee.

Trust boundaries

EdgeTrustedValidated at the edge
Caller → controllerNothing about the caller’s intent.require_auth on the acting address; pause state; account ownership where required; all risk invariants.
Governance → controllerGovernance is the controller owner in production.Timelock delay before admin mutations execute; proposal-time input validation on governance.
Controller → poolThe pool is the protocol’s own contract, owner-set to the controller.Every mutating pool method is #[only_owner]. The pool re-checks reserves, caps, and utilization itself.
Controller → oracleReflector and RedStone as price sources.Every response: staleness, future skew, deviation bands, dual-source agreement, and sanity bounds, under a per-flow policy. Fail-closed.
Controller → aggregatorThe aggregator address (governance-set) to execute a swap.The swap payload is opaque Bytes, forwarded verbatim. The controller enforces amount > 0, non-empty bytes, input not overspent, and positive output delta.
Controller → Blend poolGovernance-approved Blend pool addresses only.is_blend_pool_approved check; balance-delta reconciliation on debt repay refunds.
Pool → tokenListed SAC / SEP-41 tokens behave as standard transfer assets.Input-side flows credit measured balance; output-side flows rely on standard transfer semantics.
Pool → flash receiverThe receiver runs arbitrary callback logic.The pool snapshots its balance before the callback and requires repayment of amount + fee; reentry is blocked by the shared guard.

Authorization

The controller is paused by default after construction. User and strategy entrypoints carry #[when_not_paused].

Governance boundary

ClassAuthExamples
EmergencyGovernance ownerpause, unpause (immediate).
BootstrapGovernance ownerdeploy_controller (one-time, immediate).
SchedulingPROPOSERAll propose_* entrypoints.
ExecutionEXECUTOR (optional)execute on ready operations.
CancellationCANCELLERcancel on pending operations.

Controller boundary

ClassAuthExamples
User actionscallersupply, borrow, withdraw, liquidate.
Strategy actionscaller / account ownermultiply, swap_debt, swap_collateral, repay_debt_with_collateral, migrate_from_blend, flash_loan.
Maintenancecaller (no role)update_indexes, update_account_threshold, clean_bad_debt, claim_revenue, add_rewards.
Oracle emergencyORACLE roledisable_token_oracle.
Owner configOwner (= governance)Pool deploy/upgrade, market creation, spoke risk config, governance scheduling, and global addresses. Reached via governance timelock in production.
Two permissionless flows are intentional: repay does not require the account owner (anyone may repay any account; overpayment is refunded), and liquidate is open to any liquidator on an eligible account (self-liquidation is rejected, #13). Ownership transfer is two-step on both contracts: transfer_ownership(new_owner, live_until_ledger) then accept_ownership(). Controller accept_ownership syncs the access-control admin and ORACLE role grants.

Reentry guard

A shared single-flight guard, FlashLoanOngoing, blocks reentry during external code execution:
  • Flash-loan callbacks.
  • Strategy swaps through the aggregator.
  • Blend submit calls during migration.
Mutating controller entrypoints reject calls while the guard is set, raising #400 FlashLoanOngoing.

Oracle risk

The oracle path is fail-closed: a provider trap reverts the whole transaction. Risk-increasing flows and liquidation run under strict policies that reject staleness, unsafe deviation, and degraded dual-source agreement. Risk-decreasing flows and views run under permissive policies. See Oracles for the five policies and tolerance math.

Token assumptions

Listed tokens must be standard SAC / SEP-41 contracts with fixed-balance, 1:1 transfer semantics. Do not list fee-on-transfer, rebasing, or hook-bearing tokens without a coordinated migration.

Pool trust

The pool is deliberately narrow:
  • It performs no account-level solvency decisions.
  • Its mutating methods are owner-only.
  • It never calls another pool.
  • It emits no events.
reserves(asset) returns protocol-tracked cash, not live token balance. Live balance is used only for flash-loan settlement checks.

Upgrade model

Controller and pool upgrades are governance-timelocked in production. The controller’s upgrade auto-pauses first. Pool rate-model updates accrue at the old rate before applying new parameters. There is no generic storage-migration framework; layout-changing upgrades require a coordinated migration. Recommended procedure: governance pause → schedule upgrade → execute after delay → re-run required configuration → smoke-test views → unpause.

Strategy router and accumulator risk

The aggregator is governance-set and external. Slippage (total_min_out) is enforced inside the aggregator, not the controller. The accumulator is address-only trust: claimed revenue forwards to it, so verify the address before enabling claims.

Operational monitoring

Watch for assets missing AssetOracle(asset) config; oracle staleness and deviation errors; protocol_revenue rising while claim_revenue returns zero; pool reserves below withdrawal demand; supply or borrow caps near saturation; account and shared-state TTL approaching archival; token-issuer upgrades; aggregator contract changes; and timelock operations approaching expiry.

Audit status

The protocol is verified in-repo with Certora formal specifications and is pre-audit. Mainnet launch is gated by ADR 0009 and the acceptance matrix in the architecture reference.

Next

Governance

Timelock roles and emergency controls.

Deployment

Build, deploy, and bring-up sequence.

Configuration

Validation rules for risk, oracle, and spoke parameters.