LLM Notice: This documentation site supports content negotiation for AI agents. Request any page with Accept: text/markdown or Accept: text/plain header to receive Markdown instead of HTML. Alternatively, append ?format=md to any URL. All markdown files are available at /md/ prefix paths. For all content in one file, visit /llms-full.txt
Skip to main content

AutoBalancer

The AutoBalancer is FYV's core optimization engine that continuously monitors yield positions and automatically rebalances when thresholds are exceeded. This document explains how AutoBalancers work, when they trigger, and how they maintain optimal position health.

What is an AutoBalancer?

An AutoBalancer is a resource that holds yield-bearing tokens in ERC4626 vaults, monitors the ratio between current value and historical deposits, automatically withdraws excess profits or requests deficit recovery, and self-schedules continuous rebalancing at 60-second intervals.

Every YieldVault has an associated AutoBalancer stored in contract storage, managed by the vault's strategy implementation.

Core Concept: Value Ratio Monitoring

The AutoBalancer tracks two key values to determine when rebalancing is needed:

Historical Deposit Value: The cumulative value of all tokens deposited to the ERC4626 vault over time (tracked at deposit time and never changes unless new deposits occur).

Current Value: The real-time value of yield tokens held in the ERC4626 vault (increases as yield accrues and decreases if vault experiences losses).

Value Ratio:


_10
Ratio = Current Value / Historical Deposit Value

Rebalancing triggers:


_10
Ratio > 1.05 (105%) → Excess profits, withdraw surplus
_10
Ratio < 0.95 (95%) → Deficit detected, request recovery
_10
Ratio 0.95-1.05 → Within target range, no action needed

Rebalancing Mechanics

Upper Threshold: Excess Profits (Ratio > 105%)

When yield accrues and pushes the ratio above 105%, the AutoBalancer harvests profits.

What happens:

  1. Calculate excess value above 105% baseline
  2. Withdraw excess yield tokens from ERC4626 vault
  3. Swap yield tokens to collateral (FLOW) via SwapConnectors
  4. Deposit collateral to ALP Position (increases collateral, improves health factor)
  5. Update tracking (historical value stays same, current value returns to ~100%)

Example:


_22
Initial state:
_22
- Historical deposit value: $1,000
_22
- Current value: $1,080 (8% yield accrued)
_22
- Ratio: 1,080 / 1,000 = 1.08 = 108%
_22
- Threshold exceeded: 108% > 105% ✓
_22
_22
Rebalancing calculation:
_22
- Target value (105%): $1,000 × 1.05 = $1,050
_22
- Excess: $1,080 - $1,050 = $30
_22
- Withdraw: 30 YieldToken from vault
_22
_22
Swap and deposit:
_22
- Swap 30 YieldToken → 29.7 FLOW (1% slippage)
_22
- Deposit 29.7 FLOW to Position
_22
- Position collateral: 1000 → 1029.7 FLOW
_22
_22
After rebalancing:
_22
- Historical: $1,000 (unchanged)
_22
- Current: $1,050 (back near target)
_22
- Ratio: 1,050 / 1,000 = 1.05 = 105%
_22
- Collateral increased by $29.70
_22
- Profits locked in as additional safety buffer

Impact on position:


_12
Before rebalancing:
_12
- Collateral: 1000 FLOW @ $1 = $1,000
_12
- EC (80% CF): $800
_12
- Debt: 615.38 MOET
_12
- HF: 800 / 615.38 = 1.30
_12
_12
After rebalancing:
_12
- Collateral: 1029.7 FLOW @ $1 = $1,029.70
_12
- EC (80% CF): $823.76
_12
- Debt: 615.38 MOET (unchanged)
_12
- HF: 823.76 / 615.38 = 1.34 (improved!)
_12
- Safety buffer increased

Lower Threshold: Deficit Recovery (Ratio < 95%)

When the vault experiences losses or value drops below 95%, the AutoBalancer requests recovery funds.

What happens:

  1. Calculate deficit below 95% baseline
  2. Request deficit value from ALP Position
  3. Position provides MOET (either from available liquidity or by borrowing more)
  4. Swap MOET to yield tokens via SwapConnectors
  5. Deposit yield tokens to ERC4626 vault
  6. Update tracking (current value returns to ~100%)

Example:


_22
State after vault loss:
_22
- Historical deposit value: $1,000
_22
- Current value: $920 (8% loss in vault)
_22
- Ratio: 920 / 1,000 = 0.92 = 92%
_22
- Threshold breached: 92% < 95% ✓
_22
_22
Rebalancing calculation:
_22
- Target value (95%): $1,000 × 0.95 = $950
_22
- Deficit: $950 - $920 = $30
_22
- Request: $30 from Position
_22
_22
Position response:
_22
- Position has available liquidity: provides 30 MOET
_22
- OR Position borrows additional 30 MOET if needed
_22
- Swap 30 MOET → 29.7 YieldToken
_22
- Deposit 29.7 YieldToken to vault
_22
_22
After rebalancing:
_22
- Historical: $1,000 (unchanged)
_22
- Current: $920 + $29.70 = $949.70
_22
- Ratio: 949.70 / 1,000 = 0.9497 ≈ 95%
_22
- Deficit recovered

Impact on leveraged positions:


_12
Before deficit recovery:
_12
- Collateral: 1000 FLOW @ $1 = $1,000
_12
- Debt: 615.38 MOET
_12
- Vault value: $920 (loss)
_12
- Total exposure: $1,000 + $920 = $1,920
_12
_12
After deficit recovery (if Position borrowed more):
_12
- Collateral: 1000 FLOW (unchanged)
_12
- Debt: 615.38 + 30 = 645.38 MOET (increased)
_12
- Vault value: $949.70 (recovered)
_12
- Total exposure: $1,000 + $949.70 = $1,949.70
_12
- HF: 800 / 645.38 = 1.24 (slightly lower but still safe)

Rebalancing Flow Diagram


_28
graph TD
_28
Start[Rebalance Triggered] --> GetBalance[Get current vault balance]
_28
GetBalance --> GetHistorical[Get historical deposit value]
_28
GetHistorical --> CalcRatio[Calculate ratio]
_28
CalcRatio --> CheckRatio{Ratio?}
_28
_28
CheckRatio -->|> 1.05| Excess[Excess Profits]
_28
CheckRatio -->|< 0.95| Deficit[Deficit Recovery]
_28
CheckRatio -->|0.95-1.05| NoAction[No Action Needed]
_28
_28
Excess --> CalcExcess[Calculate excess amount]
_28
CalcExcess --> WithdrawVault[Withdraw from ERC4626]
_28
WithdrawVault --> SwapToFlow[Swap YieldToken → FLOW]
_28
SwapToFlow --> DepositPos[Deposit FLOW to Position]
_28
DepositPos --> Schedule[Schedule next rebalance]
_28
_28
Deficit --> CalcDeficit[Calculate deficit amount]
_28
CalcDeficit --> RequestMOET[Request MOET from Position]
_28
RequestMOET --> SwapToYield[Swap MOET → YieldToken]
_28
SwapToYield --> DepositVault[Deposit to ERC4626]
_28
DepositVault --> Schedule
_28
_28
NoAction --> Schedule
_28
Schedule --> End[Complete]
_28
_28
style Excess fill:#bfb
_28
style Deficit fill:#fbb
_28
style NoAction fill:#bbf

Self-Scheduling Mechanism

AutoBalancers implement perpetual automation through self-scheduling, eliminating the need for external bots or keepers.

How It Works

Initial Schedule: When a YieldVault is created, the AutoBalancer is registered in the SchedulerRegistry and scheduled for its first execution via FlowTransactionScheduler at T+60 seconds.

Execution: At scheduled time, scheduler calls autoBalancer.rebalance(). The AutoBalancer performs its rebalancing logic (check ratio, execute if needed).

Reschedule: At the end of rebalance(), the AutoBalancer calls scheduleNextRebalance() to schedule the next execution 60 seconds later.

Perpetual Loop: This creates an infinite self-scheduling loop where each execution schedules the next one.

Sequence diagram:


_22
sequenceDiagram
_22
participant Scheduler
_22
participant AB as AutoBalancer
_22
participant Registry
_22
_22
Note over AB: Initial vault creation
_22
AB->>Registry: registerAutoBalancer()
_22
AB->>Scheduler: schedule(rebalance, T+60s)
_22
_22
Note over Scheduler: Wait 60 seconds
_22
_22
Scheduler->>AB: rebalance()
_22
AB->>AB: Check ratio and execute logic
_22
AB->>Scheduler: schedule(rebalance, T+60s)
_22
_22
Note over Scheduler: Wait 60 seconds
_22
_22
Scheduler->>AB: rebalance()
_22
AB->>AB: Check ratio and execute logic
_22
AB->>Scheduler: schedule(rebalance, T+60s)
_22
_22
Note over AB,Scheduler: Loop continues indefinitely...

Atomic Registration

Vault creation and AutoBalancer registration happen atomically:


_13
transaction createVault() {
_13
prepare(signer: AuthAccount) {
_13
// All these steps happen in one transaction
_13
let vault <- createYieldVault(...)
_13
let autoBalancer <- createAutoBalancer(...)
_13
_13
registerInRegistry(autoBalancer) // Step 1
_13
scheduleFirstRebalance(autoBalancer) // Step 2
_13
_13
// If ANY step fails, entire transaction reverts
_13
// No orphaned or incomplete vaults
_13
}
_13
}

This guarantees that every vault either has a fully functional AutoBalancer with scheduled execution or doesn't exist at all (no partial creation).

Configuration Parameters

AutoBalancers accept configuration to control their behavior:


_14
pub struct AutoBalancerConfig {
_14
// Rebalancing thresholds
_14
pub let upperThreshold: UFix64 // Default: 1.05 (105%)
_14
pub let lowerThreshold: UFix64 // Default: 0.95 (95%)
_14
_14
// Scheduling
_14
pub let rebalanceIntervalSeconds: UInt64 // Default: 60
_14
_14
// Swap protection
_14
pub let slippageTolerance: UFix64 // Default: 0.01 (1%)
_14
_14
// ERC4626 vault
_14
pub let vaultAddress: Address // Target yield vault
_14
}

Tuning considerations:

Tighter thresholds (e.g., 1.02/0.98): More frequent rebalancing, higher gas costs, more precise optimization, better capital efficiency.

Wider thresholds (e.g., 1.10/0.90): Less frequent rebalancing, lower gas costs, more yield variance tolerance, lower capital efficiency.

Shorter intervals (e.g., 30s): More responsive to changes, higher gas costs, better for volatile vaults.

Longer intervals (e.g., 300s): Less responsive, lower gas costs, better for stable vaults.

Gas Optimization

AutoBalancers are designed to minimize gas costs while maintaining effectiveness:

No-Op When In Range: If ratio is within 95%-105%, the rebalance function returns early without executing swaps or transactions, costing minimal gas.

Batch Operations: When rebalancing is needed, all operations (withdraw, swap, deposit) happen in a single transaction.

Efficient Scheduling: Uses Flow's native transaction scheduler rather than external keeper networks.

Bounded Execution: Each rebalance has deterministic gas cost based on operations performed.

Example gas costs (approximate):


_10
Rebalance (no action needed): ~0.0001 FLOW
_10
Rebalance (with excess withdrawal): ~0.001 FLOW
_10
Rebalance (with deficit recovery): ~0.0015 FLOW

Monitoring AutoBalancer State

Users can query AutoBalancer state to understand position health:

Scripts

Get current ratio:


_12
import FlowYieldVaults from 0xFlowYieldVaults
_12
_12
pub fun main(vaultID: UInt64): UFix64 {
_12
let vault = FlowYieldVaults.getVault(id: vaultID)
_12
let autoBalancer = vault.getAutoBalancer()
_12
_12
let current = autoBalancer.getCurrentValue()
_12
let historical = autoBalancer.getHistoricalValue()
_12
_12
return current / historical
_12
}
_12
// Returns: 1.08 (108% ratio)

Get rebalancing history:


_10
pub fun main(vaultID: UInt64): [RebalanceEvent] {
_10
let vault = FlowYieldVaults.getVault(id: vaultID)
_10
let autoBalancer = vault.getAutoBalancer()
_10
_10
return autoBalancer.getRebalanceHistory()
_10
}
_10
// Returns array of past rebalancing events with timestamps and amounts

Check next rebalance time:


_10
pub fun main(vaultID: UInt64): UFix64 {
_10
let registry = FlowYieldVaults.getSchedulerRegistry()
_10
return registry.getNextScheduledTime(vaultID: vaultID)
_10
}
_10
// Returns: 1703001234 (Unix timestamp)

Recovery Mechanisms

If an AutoBalancer becomes stuck or fails to self-schedule, the Supervisor provides recovery:

Supervisor Recovery: Scans SchedulerRegistry for vaults with pending schedules, attempts to re-seed scheduling for stuck vaults (max 50 per execution), and automatically reschedules itself if more work remains.

Manual Recovery: Vault owners can manually trigger rebalancing via forceRebalance() transaction if AutoBalancer is stuck.

Admin Recovery: Protocol administrators can intervene in case of critical failures using admin capabilities.

Learn more in Scheduling System.

Advanced: Custom Rebalancing Logic

Developers can implement custom AutoBalancer logic for specialized strategies:


_12
pub resource CustomAutoBalancer: AutoBalancerInterface {
_12
pub fun rebalance() {
_12
// Custom logic
_12
// - Different thresholds based on time of day
_12
// - Multi-vault coordination
_12
// - Dynamic threshold adjustment
_12
// - Alternative profit distribution
_12
_12
// Must call scheduleNextRebalance() at end
_12
self.scheduleNextRebalance()
_12
}
_12
}

Custom implementations must maintain the self-scheduling mechanism and implement the AutoBalancerInterface and register with SchedulerRegistry.

Best Practices

Monitor Ratio: Keep an eye on your AutoBalancer's value ratio. Frequent rebalancing in one direction indicates systematic vault performance.

Understand Triggers: Know your thresholds. If your vault frequently hits 105%, you're generating steady profits. If it hits 95%, the vault may be experiencing losses.

Gas Awareness: More frequent rebalancing = more gas costs. Balance responsiveness vs. costs based on your vault size.

Threshold Tuning: Larger vaults benefit from tighter thresholds (better optimization). Smaller vaults benefit from wider thresholds (lower gas impact).

Track History: Review rebalancing history to understand vault performance patterns and identify optimal strategy configurations.

Summary

AutoBalancers are FYV's optimization engine that maintain yield positions within target ranges. They monitor value ratios continuously (every 60 seconds), withdraw excess profits above 105% and lock them as collateral, request deficit recovery below 95% to maintain position health, and self-schedule perpetually without external dependencies.

Key mechanisms:

  • Value ratio monitoring: Current vs. Historical
  • Bidirectional rebalancing: Excess profits and deficit recovery
  • Self-scheduling: Perpetual 60-second loops
  • Atomic registration: All-or-nothing vault creation
  • Gas optimization: No-op when within range

Next Steps


Key Takeaway

AutoBalancers continuously optimize your yield position by harvesting profits when value exceeds 105% and recovering deficits when value drops below 95%. This creates a self-optimizing system that locks in gains and prevents losses without manual intervention.