Skip to main content

Sable Turbo

The Turbo vault (internally named btcvault.cairo) executes a 3-loop USDC leverage strategy to maximize BTC exposure and lending yield.
Risk Level: 5 (High)
Strategy: 3x loop: Deposit WBTC → Borrow USDC → Swap USDC→WBTC → Re-deposit → Repeat

Strategy Overview

Loop 1: Deposit 1.0 WBTC → Borrow 0.5 BTC worth of USDC → Swap to 0.5 WBTC → Re-deposit
Loop 2: Deposit 0.5 WBTC → Borrow 0.25 BTC worth of USDC → Swap to 0.25 WBTC → Re-deposit
Loop 3: Deposit 0.25 WBTC → Final collateral position (no borrow)

Result: ~1.75 WBTC collateral, ~0.75 BTC worth of USDC debt
        Net exposure: 1.0 WBTC input → ~1.75x leveraged BTC position

How It Works

  1. Deposit WBTC to Vesu PRIME pool as collateral
  2. Borrow USDC at 50% LTV using Pragma Oracle BTC price
  3. Swap USDC → WBTC via AVNU (Ekubo USDC/WBTC pool)
  4. Re-deposit swapped WBTC as additional collateral
  5. Repeat steps 2-4 two more times
Final state: ~1.75x BTC exposure for every 1 WBTC deposited, amplifying both yield and price movement.

Yield Sources

SourceTypeAmplification
Vesu Lending APYBase yield1.75x (all collateral earns interest)
BTCFi STRK RewardsIncentive1.75x (rewards on all collateral)
BTC Price AppreciationCapital gain1.75x upside (and downside)
USDC Borrow CostExpense-0.75x (reduces net yield)
Example: If Vesu WBTC supply APY = 10% and USDC borrow APY = 5%:
Net APY ≈ (1.75 × 10%) - (0.75 × 5%) + BTCFi rewards ≈ 13.75% + STRK

Risk Profile

  • High leverage: ~1.75x BTC exposure amplifies both gains and losses
  • Liquidation risk: If BTC drops >30%, positions could be liquidated
  • Borrow cost risk: If USDC borrow APY > WBTC supply APY, vault loses money
  • Swap slippage: Each loop incurs USDC→WBTC swap slippage (mitigated by Ekubo)

Contract Architecture

File: btcvault.cairo (~900 LOC)
Deployed: 0x05f3d02005027296ccfb90574544b941d9ddc55c673e6fe0e92cb6f07e68d1f7

Core Components

Turbo auto-executes 3 leverage loops on every deposit:
fn _deploy_to_strategy(ref self: ContractState, amount: u256) {
    let num_loops: u8 = 3;
    let mut remaining_wbtc = amount;
    let mut total_collateral: u256 = 0;
    let mut total_debt: u256 = 0;
    let mut i: u8 = 0;

    while i < num_loops {
        // Calculate USDC borrow at 50% LTV using oracle price
        let borrow_usdc = (remaining_wbtc * wbtc_price * 50) / (usdc_price * 10000);
        if borrow_usdc == 0 { break; }

        // Deposit WBTC collateral + borrow USDC atomically
        wbtc.approve(pool_addr, remaining_wbtc);
        vesu.modify_position(ModifyPositionParams {
            collateral: Amount { /* deposit WBTC */ },
            debt: Amount { /* borrow USDC */ },
        });
        total_collateral += remaining_wbtc;
        total_debt += borrow_usdc;

        // Swap USDC -> WBTC via AVNU (Ekubo USDC/WBTC pool)
        usdc.approve(avnu_addr, borrow_usdc);
        avnu.multi_route_swap(
            usdc_addr, borrow_usdc, wbtc_addr,
            0, min_wbtc_out, this, 0, Zero::zero(), routes,
        );

        i += 1;
        remaining_wbtc = wbtc.balance_of(this);
        if remaining_wbtc < min_deposit { break; }
    };

    // Deposit leftover WBTC (final collateral, no borrow)
    // ...
}
From btcvault.cairo:981

Key Functions

User Functions

deposit
function
Deposit WBTC and receive yvBTC sharesParameters:
  • assets (u256): Amount of WBTC to deposit (must be ≥ 4× min_deposit)
  • receiver (ContractAddress): Address to receive vault shares
Returns: u256 — Number of shares minted
Auto-executes 3-loop strategy:
  1. Loop 1: 100% WBTC → 50% USDC borrow → ~48% WBTC (2% slippage)
  2. Loop 2: 48% WBTC → 24% USDC borrow → ~23% WBTC
  3. Loop 3: 23% WBTC → final collateral (no borrow)
Final: ~1.71 WBTC collateral, ~0.74 BTC worth of USDC debt
withdraw
function
Burn shares and withdraw WBTC (flash loan unwind)Parameters:
  • assets (u256): Amount of WBTC to withdraw
  • receiver (ContractAddress): Address to receive WBTC
  • owner (ContractAddress): Share owner
Returns: u256 — Number of shares burned
Automatic Flash Loan Unwind:
  • Flash borrows full USDC debt from Vesu (zero fee)
  • Repays debt + withdraws all WBTC collateral
  • Swaps ~5% WBTC → USDC via AVNU to repay flash loan
  • User receives remaining WBTC (~95%)
total_assets
view
Get net WBTC value managed by vault (collateral - debt)
fn total_assets(self: @ContractState) -> u256
Calculation:
fn _refresh_total_assets(ref self: ContractState) {
    let (_position, collateral_value, debt_value) = vesu.position(wbtc_addr, usdc_addr, this);
    let idle_wbtc = wbtc.balance_of(this);

    // Convert USDC debt to WBTC equivalent
    let debt_in_wbtc = (debt_value * usdc_price * 100) / wbtc_price;

    let gross = collateral_value + idle_wbtc;
    let net = if debt_in_wbtc < gross { gross - debt_in_wbtc } else { 0 };

    self.total_assets_managed.write(net);
}
From btcvault.cairo:928

Curator Functions

execute_leverage_loop
function
Manually execute leverage loop (for idle WBTC)Parameters:
  • collateral_amount (u256): WBTC to deposit
  • borrow_amount (u256): USDC to borrow
  • min_swap_out (u256): Minimum WBTC from USDC swap (slippage protection)
  • routes (Array<Route>): AVNU swap routes
fn execute_leverage_loop(
    ref self: ContractState,
    collateral_amount: u256,
    borrow_amount: u256,
    min_swap_out: u256,
    routes: Array<Route>,
)
Implementation at btcvault.cairo:635
deleverage
function
Manually reduce leverage (repay debt + withdraw collateral)Parameters:
  • repay_amount (u256): USDC to repay
  • withdraw_collateral (u256): WBTC to withdraw
fn deleverage(ref self: ContractState, repay_amount: u256, withdraw_collateral: u256)
Implementation at btcvault.cairo:726
Leaves 2 microUSDC buffer to avoid Vesu rounding errors.
flash_unwind
function
Atomically close all leveraged positions via flash loanParameters:
  • wbtc_to_sell (u256): WBTC to swap for USDC flash loan repayment
  • min_usdc_out (u256): Minimum USDC from swap
  • routes (Array<Route>): AVNU swap routes
fn flash_unwind(ref self: ContractState, wbtc_to_sell: u256, min_usdc_out: u256, routes: Array<Route>)
Implementation at btcvault.cairo:791Used internally by _ensure_idle_balance during withdrawals.

Integration with External Protocols

Vesu PRIME Pool

Pool ID: 0x0451fe483d5921a2919ddd81d0de6696669bccdacd859f72a4fba7656b97c3b5 Turbo uses Vesu PRIME for:
  • Collateral: WBTC (earns lending APY + BTCFi rewards)
  • Debt: USDC borrowed at 50% LTV
vesu.modify_position(ModifyPositionParams {
    collateral_asset: wbtc_addr,
    debt_asset: usdc_addr,
    user: vault_address,
    collateral: Amount { /* WBTC amount */ },
    debt: Amount { /* USDC amount, positive = borrow */ },
});

AVNU USDC/WBTC Swap

Turbo swaps USDC → WBTC on every loop via Ekubo USDC/WBTC pool (fee: 0.05%, tick_spacing: 1000):
let swap_params: Array<felt252> = array![
    token0, token1,              // WBTC, USDC (sorted by address)
    0x20c49ba5e353f80000000000000000, // fee 0.05%
    0x3e8,                       // tick_spacing 1000
    0x0,                         // extension
    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, // sqrt_ratio_distance (max)
];

let routes = array![Route {
    token_from: usdc_addr,
    token_to: wbtc_addr,
    exchange_address: ekubo_core,
    percent: 1000000000000,  // 100%
    additional_swap_params: swap_params.span(),
}];

avnu.multi_route_swap(
    usdc_addr, usdc_balance, wbtc_addr,
    0, min_wbtc_out, vault_address, 0, Zero::zero(), routes,
);
From btcvault.cairo:1051 Ekubo Core Address: 0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b

Vesu Price Oracle

Turbo reads BTC and USDC prices from Vesu’s Pragma integration:
let wbtc_price = vesu.price(wbtc_addr);  // Returns PriceWithDecimals { value: u256, decimals: u8 }
let usdc_price = vesu.price(usdc_addr);
Prices have 8 decimals (e.g., BTC = $95,000 → value = 9,500,000,000,000).

Example Usage

Depositing WBTC

import { Contract } from 'starknet'

const turboVault = new Contract(turboABI, turboAddress, account)
const wbtc = new Contract(erc20ABI, wbtcAddress, account)

// Check minimum deposit (4× Vesu dust for 3 loops)
const minDeposit = await turboVault.min_deposit()
console.log('Min deposit:', minDeposit / 1e8, 'WBTC')

// Deposit (auto-executes 3x leverage loop)
const amount = 100_000_000n // 1.0 WBTC
await wbtc.approve(turboAddress, amount)
await turboVault.deposit(amount, account.address)

// Check yvBTC balance
const shares = await turboVault.balance_of(account.address)
const sharePrice = await turboVault.convert_to_assets(1e8)  // Price per 1 share
console.log('Share price:', sharePrice / 1e8, 'WBTC per share')

Checking Leverage Stats

// Strategy info: (collateral, debt, loops, paused)
const [wbtcCollateral, usdcDebt, numLoops, paused] = await turboVault.get_strategy_info()

console.log('WBTC collateral:', wbtcCollateral / 1e8)
console.log('USDC debt:', usdcDebt / 1e6)
console.log('Leverage loops:', numLoops)

// Calculate leverage ratio
const btcPrice = 95000 // Example: $95k/BTC
const collateralValue = (wbtcCollateral / 1e8) * btcPrice
const debtValue = usdcDebt / 1e6
const netValue = collateralValue - debtValue
const leverage = collateralValue / netValue
console.log('Effective leverage:', leverage.toFixed(2) + 'x')  // ~1.75x

// Calculate LTV
const ltv = debtValue / collateralValue
console.log('LTV:', (ltv * 100).toFixed(2) + '%')  // ~43%

Monitoring Health Factor

// Read Vesu position directly
const vesuPool = new Contract(vesuPoolABI, vesuPrimeAddress, provider)
const [position, collValue, debtValue] = await vesuPool.position(
  wbtcAddress,  // collateral_asset
  usdcAddress,  // debt_asset
  turboAddress  // user (vault)
)

// Health factor = (collateral * LT) / debt
// LT (liquidation threshold) ≈ 75% for Vesu PRIME
const healthFactor = (collValue * 75n) / (debtValue * 100n)
console.log('Health factor:', Number(healthFactor) / 100)

// Safe: health > 1.5
// Caution: health 1.0-1.5
// Danger: health < 1.0 (liquidation risk)

Withdrawing WBTC

// Get max withdrawable
const maxWithdraw = await turboVault.max_withdraw(account.address)

// Withdraw (triggers flash loan unwind)
await turboVault.withdraw(maxWithdraw, account.address, account.address)

// Flash loan process:
// 1. Flash borrow ~$45,000 USDC from Vesu (zero fee)
// 2. Repay $45,000 USDC debt + withdraw 1.75 WBTC collateral
// 3. Swap 0.05 WBTC -> $4,750 USDC via AVNU (enough to repay flash loan)
// 4. User receives ~1.70 WBTC (1.0 initial + 0.7 profit)

Security Considerations

Risks

  1. Liquidation Risk: If BTC drops >30% rapidly, USDC debt could exceed 75% of collateral value → liquidation
  2. Negative Yield Risk: If USDC borrow APY > WBTC supply APY, vault loses money
  3. Swap Slippage: Each loop loses ~2% to USDC→WBTC swap fees (Ekubo 0.05% + slippage)
  4. Flash Loan Dependency: Withdrawals require Vesu flash loans (zero fee, but dependency)
  5. Oracle Risk: Vesu’s Pragma price feed failure could cause incorrect borrows
  6. Amplified BTC Volatility: 1.75x exposure means 1.75x gains and losses on BTC price moves

Risk Mitigation

  • 50% LTV buffer: Initial LTV ~43%, leaving 32% margin before 75% liquidation
  • Conservative loop count: 3 loops (not 5-10) limits extreme leverage
  • Hardcoded Ekubo route: Uses highest-liquidity USDC/WBTC pool (minimal slippage)
  • Flash loan safety: Only vault can initiate on_flash_loan callback
  • Vesu flash loans: Zero fee, always available (backed by idle pool liquidity)
  • Pause mechanism: Owner can halt deposits if yield turns negative

Liquidation Price Calculation

// Example: 1.0 WBTC deposit -> 1.75 WBTC collateral, $45k USDC debt
const initialBTC = 95000 // $95k/BTC
const collateral_wbtc = 1.75
const debt_usd = 45000

// Liquidation occurs at 75% LTV:
// debt / (collateral * liquidation_price) = 0.75
// liquidation_price = debt / (collateral * 0.75)
const liquidationPrice = debt_usd / (collateral_wbtc * 0.75)
console.log('Liquidation price:', '$' + liquidationPrice.toFixed(0))  // ~$34,286

// Price drop to liquidation:
const priceDrop = ((initialBTC - liquidationPrice) / initialBTC) * 100
console.log('Liquidation if BTC drops:', priceDrop.toFixed(1) + '%')  // ~63.9% drop

Additional Resources

Build docs developers (and LLMs) love