FCM Architecture Overview
This document explains how Flow Credit Market's (FCM) three core components - Automated Lending Platform (ALP), Flow Yield Vaults (FYV), and Medium Of Exchange Token (MOET) - integrate to create a complete yield-generating system with automated liquidation prevention.
FCM's architecture is designed for composability and automation. Each component has clear responsibilities and communicates through standardized interfaces (DeFi Actions), enabling:
- Independent development and upgrades
- Third-party strategy integrations
- System resilience through modularity
High-Level Architecture
_47graph TB_47 subgraph "User Interface"_47 User[User/dApp]_47 end_47_47 subgraph "FCM System"_47 subgraph ALP["ALP Components"]_47 Pool[Pool Contract]_47 Position[Position]_47 Oracle[Price Oracle]_47 end_47_47 subgraph FYV["FYV Components"]_47 Strategy[Yield Strategy]_47 AutoBalancer[Auto Balancer]_47 Swapper[Token Swapper]_47 end_47_47 subgraph MOET_Layer["MOET Layer"]_47 MOET[MOET Token]_47 Pricing[Price Feeds]_47 end_47 end_47_47 subgraph "External Protocols"_47 DEX[DEXes/AMMs]_47 Farm[Yield Farms]_47 LP[Liquidity Pools]_47 end_47_47 User -->|Deposit Collateral| Position_47 Position -->|Auto-borrow| MOET_47 MOET -->|Via DrawDownSink| Strategy_47 Strategy -->|Deploy| DEX_47 Strategy -->|Deploy| Farm_47 Strategy -->|Deploy| LP_47_47 Oracle -->|MOET-denominated prices| Pool_47 Pricing -->|Price data| Oracle_47_47 AutoBalancer -->|Manage exposure| Strategy_47 Strategy -->|Via TopUpSource| Position_47_47 style ALP fill:#4a7abf,stroke:#333,stroke-width:2px,color:#fff_47 style FYV fill:#4d994d,stroke:#333,stroke-width:2px,color:#fff_47 style MOET_Layer fill:#d94d4d,stroke:#333,stroke-width:2px,color:#fff_47 style MOET fill:#d94d4d,stroke:#333,stroke-width:2px,color:#fff
Component Integration
1. ALP ↔ MOET Integration
Purpose: MOET serves as the unit of account and primary borrowed asset for ALP.
Integration points:
_10ALP Pool_10├── defaultToken: Type<@MOET.Vault>_10├── priceOracle: Returns prices in MOET terms_10├── Auto-borrowing: Borrows MOET_10└── Debt tracking: Denominated in MOET
Key interactions:
-
Price Quotation: All token prices quoted in MOET
_10FLOW/MOET: 1.0_10USDC/MOET: 1.0_10stFLOW/MOET: 1.05 -
Health Calculations: All in MOET terms
_10Effective Collateral = FLOW amount × FLOW/MOET price × collateral factor_10Effective Debt = MOET borrowed_10Health Factor = Effective Collateral / Effective Debt -
Auto-Borrowing: Always borrows MOET
_10User deposits → ALP calculates capacity → Borrows MOET → User receives MOET
2. ALP ↔ FYV Integration
Purpose: FYV receives borrowed funds from ALP and provides liquidity for liquidation prevention.
Integration via DeFi Actions:
_10ALP Position_10├── DrawDownSink → FYV Strategy (when overcollateralized)_10└── TopUpSource ← FYV Strategy (when undercollateralized)
Interaction flow:
Overcollateralized (HF > 1.5)
_13Position detects: HF = 1.8 (too high)_13↓_13Position calculates: Can borrow $200 more MOET_13↓_13Position borrows: 200 MOET from Pool_13↓_13Position pushes: 200 MOET → DrawDownSink_13↓_13DrawDownSink = FYV Strategy_13↓_13FYV Strategy swaps: 200 MOET → 200 YieldToken_13↓_13AutoBalancer holds: YieldToken, generates yield
Undercollateralized (HF < 1.1)
_13Position detects: HF = 1.05 (too low)_13↓_13Position calculates: Need to repay $150 MOET_13↓_13Position pulls: Request 150 MOET from TopUpSource_13↓_13TopUpSource = FYV Strategy_13↓_13FYV Strategy swaps: 150 YieldToken → 150 MOET_13↓_13Position repays: 150 MOET to Pool_13↓_13New HF: 1.3 (restored to target)
Code integration:
The integration is implemented through DeFi Actions interfaces in Cadence. On the ALP side, each Position holds references to a DrawDownSink and TopUpSource, which are called during rebalancing operations. When the position becomes overcollateralized, it borrows additional funds and pushes them to the DrawDownSink. When undercollateralized, it pulls funds from the TopUpSource to repay debt.
_17// ALP side (simplified)_17access(all) struct Position {_17 access(self) var drawDownSink: {DeFiActions.Sink}?_17 access(self) var topUpSource: {DeFiActions.Source}?_17_17 // When overcollateralized_17 fun rebalanceDown() {_17 let borrowed <- pool.borrow(amount: excessCapacity)_17 self.drawDownSink?.deposit(vault: <-borrowed)_17 }_17_17 // When undercollateralized_17 fun rebalanceUp() {_17 let repayment <- self.topUpSource?.withdraw(amount: shortfall)_17 pool.repay(vault: <-repayment)_17 }_17}
On the FYV side, strategies implement the DeFi Actions interfaces by providing Sink and Source creation functions. These functions return objects that handle the swap between MOET and yield-bearing tokens. When ALP calls the Sink, FYV converts MOET into yield tokens. When ALP pulls from the Source, FYV converts yield tokens back to MOET.
TracerStrategy is one of FYV's yield strategies that tracks and manages positions in external yield-generating protocols. It acts as the bridge between ALP's lending system and external DeFi opportunities, automatically converting between MOET and yield tokens while maintaining the optimal balance for returns.
_11// FYV side (simplified)_11access(all) struct TracerStrategy {_11 // Implements DeFi Actions interfaces_11 access(all) fun createSink(): {DeFiActions.Sink} {_11 // Returns sink that swaps MOET → YieldToken_11 }_11_11 access(all) fun createSource(): {DeFiActions.Source} {_11 // Returns source that swaps YieldToken → MOET_11 }_11}
3. FYV ↔ MOET Integration
Purpose: MOET is the medium of exchange between FYV and external yield sources.
Flow:
_10FYV receives MOET → Swaps to target token → Deploys to yield source_10↓_10Time passes, yield accumulates_10↓_10When needed: Exit yield source → Swap to MOET → Return to ALP
Example with TracerStrategy:
_161. Receive MOET from ALP_16 ├── DrawDownSink.deposit(moetVault)_16_162. Swap MOET → YieldToken_16 ├── Swapper.swap(moet → yieldToken)_16 └── AutoBalancer.hold(yieldToken)_16_163. Generate yield_16 ├── YieldToken appreciates_16 ├── Farming rewards accrue_16 └── Trading fees accumulate_16_164. Provide back to ALP (when needed)_16 ├── AutoBalancer.release(yieldToken)_16 ├── Swapper.swap(yieldToken → moet)_16 └── TopUpSource.withdraw() returns MOET
Data Flow Architecture
User Deposit Flow
_14graph LR_14 User[👤 You deposit<br/>1000 FLOW] --> Position[ALP Position<br/>stores collateral]_14 Position --> Oracle[Oracle checks<br/>FLOW price]_14 Oracle --> Health{Health Factor<br/>calculation}_14 Health -->|HF > 1.5<br/>Can borrow| Borrow[Auto-borrow<br/>615 MOET]_14 Health -->|HF ≤ 1.5<br/>No borrowing| Done1[✅ Deposit complete]_14 Borrow --> DrawDown[Push to<br/>DrawDownSink]_14 DrawDown --> FYV[FYV Strategy<br/>swaps to yield tokens]_14 FYV --> Done2[✅ Earning yield<br/>HF = 1.3]_14_14 style Position fill:#4a7abf,stroke:#333,stroke-width:2px,color:#fff_14 style FYV fill:#4d994d,stroke:#333,stroke-width:2px,color:#fff_14 style Borrow fill:#d94d4d,stroke:#333,stroke-width:2px,color:#fff_14 style Done2 fill:#4d994d,stroke:#333,stroke-width:2px,color:#fff
Price Drop & Rebalancing Flow
_13graph LR_13 Drop[📉 FLOW price<br/>drops 20%] --> Position[ALP Position<br/>detects HF = 1.05]_13 Position --> Calc[Calculate needed<br/>repayment: 123 MOET]_13 Calc --> TopUp[Pull from<br/>TopUpSource]_13 TopUp --> FYV[FYV Strategy<br/>swaps yield tokens]_13 FYV --> MOET[Returns<br/>123 MOET]_13 MOET --> Repay[ALP repays<br/>debt to Pool]_13 Repay --> Restored[✅ Health restored<br/>HF = 1.3]_13_13 style Position fill:#4a7abf,stroke:#333,stroke-width:2px,color:#fff_13 style FYV fill:#4d994d,stroke:#333,stroke-width:2px,color:#fff_13 style MOET fill:#d94d4d,stroke:#333,stroke-width:2px,color:#fff_13 style Restored fill:#4d994d,stroke:#333,stroke-width:2px,color:#fff
Component Responsibilities
ALP Responsibilities
| Function | Description |
|---|---|
| Position Management | Create, track, and manage user positions |
| Collateral Tracking | Monitor deposited collateral using scaled balances |
| Debt Tracking | Track borrowed amounts with interest accrual |
| Health Monitoring | Calculate and monitor position health factors |
| Auto-Borrowing | Automatically borrow MOET when overcollateralized |
| Auto-Repayment | Automatically repay when undercollateralized |
| Liquidation | Handle traditional liquidations if auto-repayment fails |
| Interest Calculation | Accrue interest on borrowed amounts |
| Oracle Integration | Query prices for health calculations |
FYV Responsibilities
| Function | Description |
|---|---|
| Strategy Management | Implement and manage yield strategies |
| Capital Deployment | Deploy received MOET to yield sources |
| Yield Generation | Generate returns through various mechanisms |
| Token Swapping | Swap between MOET and yield tokens |
| Auto-Balancing | Maintain optimal exposure to yield tokens |
| Liquidity Provision | Provide MOET when ALP needs rebalancing |
| Risk Management | Monitor and adjust strategy parameters |
| Yield Compounding | Reinvest returns for compound growth |
MOET Responsibilities
| Function | Description |
|---|---|
| Unit of Account | Provide standardized pricing unit |
| Value Transfer | Enable value flow between ALP and FYV |
| Price Stability | Maintain stable value (if stablecoin) |
| Oracle Integration | Provide price feeds for all assets |
| Liquidity | Ensure deep liquidity for swaps |
Communication Patterns
1. DeFi Actions Pattern (ALP ↔ FYV)
DeFi Actions enables ALP and FYV to communicate through standardized interfaces without tight coupling. The Sink pattern allows ALP to push borrowed funds to FYV strategies, while the Source pattern enables ALP to pull funds back when needed for rebalancing or repayment.
Sink Pattern (Push):
When ALP has excess borrowing capacity or newly borrowed funds, it uses the Sink interface to deposit MOET into FYV strategies. The FYV strategy receives the funds and automatically converts them to yield-bearing tokens.
_10// ALP pushes to FYV_10access(all) resource interface Sink {_10 access(all) fun deposit(vault: @{FungibleToken.Vault})_10}_10_10// Usage_10let sink = fyvStrategy.createSink()_10sink.deposit(vault: <-moetVault)
Source Pattern (Pull):
When ALP needs funds to maintain position health, it pulls from the Source interface. FYV converts yield tokens back to MOET and provides the requested amount, enabling automatic liquidation prevention.
_10// ALP pulls from FYV_10access(all) resource interface Source {_10 access(all) fun withdraw(amount: UFix64, type: Type): @{FungibleToken.Vault}_10}_10_10// Usage_10let source = fyvStrategy.createSource()_10let moet <- source.withdraw(amount: 100.0, type: Type<@MOET.Vault>())
2. Oracle Pattern (ALP ↔ MOET)
The Oracle pattern provides a standardized way for ALP to query token prices in MOET terms. All collateral and debt calculations use these MOET-denominated prices, ensuring consistency across the system. This enables health factor calculations and determines borrowing capacity based on real-time market data.
Price Query:
The PriceOracle interface returns the current price of any token type denominated in MOET. For example, querying the price of FLOW returns how many MOET one FLOW is worth, which ALP uses to calculate effective collateral values.
_10// ALP queries prices in MOET terms_10access(all) resource interface PriceOracle {_10 access(all) fun getPrice(token: Type): UFix64_10}_10_10// Usage_10let flowPrice = oracle.getPrice(Type<@FlowToken.Vault>())_10// Returns: 1.0 (1 FLOW = 1 MOET)
3. Event-Driven Pattern
FCM components communicate state changes through events, enabling monitoring, analytics, and external integrations. Each component emits events for significant actions like position changes, yield generation, and token operations. These events allow off-chain systems to track user activity, trigger notifications, and maintain historical records without polling smart contracts.
Key events across components:
ALP emits events for all position lifecycle operations including creation, borrowing, repayment, and rebalancing. FYV broadcasts events when deploying to strategies, generating yield, or providing liquidity back to ALP. MOET tracks token supply changes through mint and burn events, ensuring transparency in the stablecoin's circulation.
_14// ALP events_14access(all) event PositionCreated(pid: UInt64, owner: Address)_14access(all) event Borrowed(pid: UInt64, amount: UFix64)_14access(all) event Repaid(pid: UInt64, amount: UFix64)_14access(all) event Rebalanced(pid: UInt64, newHealth: UFix64)_14_14// FYV events_14access(all) event StrategyDeployed(amount: UFix64, strategy: String)_14access(all) event YieldGenerated(amount: UFix64)_14access(all) event LiquidityProvided(amount: UFix64, toALP: Bool)_14_14// MOET events_14access(all) event TokensMinted(amount: UFix64, recipient: Address)_14access(all) event TokensBurned(amount: UFix64)
System States
Normal Operation State
_11System State: Healthy_11├── ALP Positions: All HF between 1.1 and 1.5_11├── FYV Strategies: Generating yield normally_11├── MOET: Stable and liquid_11└── Oracles: Providing fresh prices_11_11Actions:_11- Accept new deposits_11- Allow withdrawals_11- Process rebalancing_11- Generate yield
Stress State (Price Volatility)
_11System State: Under Stress_11├── ALP Positions: Some HF approaching 1.1_11├── FYV Strategies: May need to provide liquidity_11├── MOET: May see increased trading volume_11└── Oracles: Prices updating frequently_11_11Actions:_11- Trigger frequent rebalancing_11- FYV provides liquidity to ALP_11- Some yield positions exited_11- Increased monitoring
Emergency State
_11System State: Emergency_11├── ALP Positions: Multiple HF < 1.0_11├── FYV Strategies: Emergency liquidation mode_11├── MOET: Potential depeg risk_11└── Oracles: Stale or unreliable_11_11Actions:_11- Circuit breakers activated_11- Liquidations triggered_11- Deposits paused_11- Admin intervention required
Scalability & Performance
Optimizations
Scaled Balance System - ALP uses a scaled balance approach that avoids updating every position when interest accrues. Instead, a single interest index update affects all positions simultaneously, making the system gas-efficient even with thousands of active positions.
Batch Rebalancing - The protocol allows multiple positions to be rebalanced in a single transaction, enabling keepers to optimize gas costs by processing several positions at once rather than submitting individual transactions for each rebalancing operation.
Lazy Evaluation - All components use lazy evaluation patterns where prices are only fetched when needed, health factors are calculated only when accessed, and interest accrues only when a position is touched. This approach minimizes unnecessary computations and reduces gas costs for operations that don't require the latest state.
Event-Driven Updates - The system emits events for all critical operations, allowing off-chain indexers to track state changes efficiently. This enables UI updates without constant blockchain queries and significantly reduces RPC load on the network while providing users with real-time information.
Limits & Constraints
| Component | Limit | Reason |
|---|---|---|
| ALP Max Positions | Configurable | Gas limits for iteration |
| FYV Strategies per Vault | ~10-20 | Complexity management |
| Rebalancing Frequency | ~1 per block | Gas and Oracle freshness |
| Max Leverage | ~5x | Safety (1.0 HF = 100%, 1.1-1.5 range) |
Security Architecture
Defense in Depth
Layer 1: Input Validation
- All user inputs sanitized
- Type checking enforced
- Capability-based access control
Layer 2: Business Logic
- Health factor checks before operations
- Minimum/maximum limits enforced
- Oracle staleness checks
Layer 3: Circuit Breakers
- Emergency pause functionality
- Liquidation warm-up periods
- Admin override capabilities
Layer 4: Economic Security
- Over-collateralization requirements
- Liquidation incentives
- Oracle price deviation limits
Layer 5: Monitoring
- Event emission for all critical operations
- Off-chain monitoring systems
- Automated alerts
Next Steps
- Understand the math: Mathematical Foundations
- Explore ALP details: ALP Architecture
- Learn about FYV: FYV Documentation
- Deep dive into MOET: MOET Documentation