Skip to main content

Architecture

RAI Dollar's contracts are organized in two layers: a per-branch layer that owns trove state and pools for a single collateral, and a singleton layer above that coordinates across branches. The diagram below mirrors the one in Aggregator.sol — hover any contract for its responsibility:

Per-branch contracts​

A branch is everything needed to run one collateral type. Branches don't share state; they share singletons.

ContractResponsibility
BorrowerOperationsUser-facing API: open / adjust / close / shield-toggle. Enforces ICR/TCR/shield rules.
TroveManagerTrove state machine: storage, ICR computation, liquidation orchestration entry-points.
LiquidationsBranch-local liquidation logic, offset against SP, redistribute remainder, batch liquidate.
RedemptionsBranch-local redemption walk over the base list and, during shutdown, the Redemption Shield cohort list.
RewardsTracks pending redistribution rewards (debt and collateral) for active troves.
StabilityPoolRD deposits that absorb liquidations. Tracks per-depositor collateral and FEE gains.
InterestEngineDrips branch debt interest. Maintains two per-second accumulators: accumulatedRate for the base book (branch_rate − v) and accumulatedShieldRate for the Redemption Shield cohort book (branch_rate + u), where the same shield-share curve produces the signed adjustment for each.
ActivePool (base)Custody of collateral backing troves without Redemption Shield.
ActiveShieldedPoolCustody of collateral backing troves with Redemption Shield enabled.
DefaultPoolCustody of redistributed collateral and debt (from liquidations that couldn't be fully offset).
CollSurplusPoolHolds collateral surplus owed back to borrowers (post-full-redemption, force-close remainders).
GasPoolHolds the per-trove 200 RD gas-compensation reserve.
SortedTroves (base)Doubly-linked list of troves without Redemption Shield, ordered by NICR.
SortedTroves (Redemption Shield cohort)Same, for troves with Redemption Shield enabled.
PriceFeedBranch-specific collateral USD price feed (Chronicle primary, Chainlink fallback; branch-specific composition for LSTs).
FeeRouterRoutes fees from this branch: between the local Stability Pool and the singleton GlobalFeeRouter.
MinTroveHelper for opening a trove exactly at MIN_NET_DEBT + gas comp with ICR = MCR.

Singleton contracts​

ContractResponsibility
AggregatorCross-branch control plane: redemption routing, drip orchestration, issuance quotas, per-branch debt EMAs, delta-rate offsets.
RDTokenThe RD stablecoin (ERC-20). Mint/burn restricted to the protocol's authorized branches and routers.
FEETokenThe FEE governance/value-capture token.
RateParControlThe PI controller that produces the system rate and par over time.
MarketOracleBalancer-hook TWAP/TWAL feed of RD/USD used by RateParControl.
GlobalFeeRouterSplits cross-branch fees between LP staking, FEE staking, keeper rewards, and bad-debt repayment.
LPStakingBPT staking; pays FEE (from LpIssuance) and RD (from direct inflows).
FEEStakingFEE staking with tiered lockups.
FLXStakingFLX staking; one-year linear FEE distribution.
GlobalHintHelperOff-chain-style hint computation across branches.
KeeperRewardsSource of keeper rewards for drip/oracle/par operations.
LpIssuance / SpIssuanceFEE emission curves for LP and SP rewards.

Branch ↔ singleton relationships​

  • Mint/burn: only BorrowerOperations, TroveManager, StabilityPool, Redemptions, and Aggregator are authorized to mint/burn RD. Authorization is role-based on RDToken.
  • Drip orchestration: Aggregator.drip() iterates every non-shutdown branch and calls each branch's TroveManager.drip(), which folds accrued interest into accumulatedRate via InterestEngine.
  • Debt EMAs: every state-changing borrower op tells the Aggregator to refresh that branch's debt EMA. The Aggregator also keeps a per-branch base debt EMA for redemption basket weights.
  • Rate offsets: the Aggregator adds a per-branch deltaRateOffset on top of the system rate from RateParControl. The offset is driven by the branch's debt-utilization error.
  • Redemption: a single Aggregator.redeemCollateral call iterates all non-shutdown branches with non-zero unshieldedDebtEma, splits the requested RD across them by EMA weight, and calls each branch's Redemptions.
  • Fees: each branch's FeeRouter collects fees, sends a (PI-controlled) fraction to the local SP, and forwards the rest to GlobalFeeRouter, which fans out to LP staking, FEE staking, keeper rewards, and bad-debt repayment.

Common message flows​

Opening a trove (Redemption Shield disabled):

Redeeming RD (multi-branch):

Liquidating a trove:

Why this shape​

  • Branches are isolated. Adding a new collateral is a new branch deploy, not a global upgrade. Branches don't read each other's pools.
  • The Aggregator is read-only with respect to branch internals. It calls into branches via well-typed interfaces; branches push debt-change notifications back via authorized calls. No shared storage.
  • The controller is a singleton. RateParControl produces one system rate and one par for all branches. Per-branch adjustment happens via the Aggregator's additive delta-rate offset, not by giving each branch its own PI loop.
  • Redemptions are multi-branch but per-branch internally. The Aggregator owns the basket-weight logic; each branch owns its own walk.