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
- Deposit WBTC to Vesu PRIME pool as collateral
- Borrow USDC at 50% LTV using Pragma Oracle BTC price
- Swap USDC → WBTC via AVNU (Ekubo USDC/WBTC pool)
- Re-deposit swapped WBTC as additional collateral
- 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
| Source | Type | Amplification |
|---|
| Vesu Lending APY | Base yield | 1.75x (all collateral earns interest) |
| BTCFi STRK Rewards | Incentive | 1.75x (rewards on all collateral) |
| BTC Price Appreciation | Capital gain | 1.75x upside (and downside) |
| USDC Borrow Cost | Expense | -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
Leverage Loop
Flash Loan Unwind
Oracle Integration
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 Withdrawals use a Vesu flash loan to atomically close all positions:fn on_flash_loan(
ref self: ContractState,
sender: ContractAddress,
asset: ContractAddress, // USDC
amount: u256, // Full USDC debt
data: Span<felt252>,
) {
// Deserialize callback data
let wbtc_to_sell: u256 = Serde::deserialize(ref data_span);
let min_usdc_out: u256 = Serde::deserialize(ref data_span);
let routes: Array<Route> = Serde::deserialize(ref data_span);
// Step 1: Repay ALL USDC debt + withdraw ALL WBTC collateral
usdc.approve(pool_addr, debt_val + debt_val / 50 + 100);
vesu.modify_position(ModifyPositionParams {
collateral: Amount { /* withdraw ALL WBTC */ },
debt: Amount { /* repay ALL USDC debt */ },
});
// Step 2: Swap WBTC -> USDC via AVNU to cover flash loan
wbtc.approve(avnu_addr, wbtc_to_sell);
avnu.multi_route_swap(
wbtc_addr, wbtc_to_sell, usdc_addr,
0, min_usdc_out, this, 0, Zero::zero(), routes,
);
// Step 3: Approve USDC for flash loan repayment pull
usdc.approve(pool_addr, amount);
// Position fully closed
self.total_collateral_deposited.write(0);
self.total_debt_borrowed.write(0);
self.leverage_loops.write(0);
}
From btcvault.cairo:485 Turbo uses Vesu’s on-chain oracle for pricing (no external Pragma dependency in loop):// Oracle prices for borrow calculation and slippage protection
let wbtc_price = vesu.price(wbtc_addr); // Vesu's Pragma feed
let usdc_price = vesu.price(usdc_addr);
assert(wbtc_price.value > 0 && usdc_price.value > 0, 'Invalid oracle prices');
// USDC to borrow (6-dec) = wbtc_sats (8-dec) * wbtc_price * 50 / (usdc_price * 10000)
let borrow_usdc = (remaining_wbtc * wbtc_price.value * 50) / (usdc_price.value * 10000);
// Min WBTC out with 5% slippage tolerance
let min_wbtc_out = (usdc_balance * usdc_price.value * 95) / wbtc_price.value;
From btcvault.cairo:996
Key Functions
User Functions
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 mintedAuto-executes 3-loop strategy:
- Loop 1: 100% WBTC → 50% USDC borrow → ~48% WBTC (2% slippage)
- Loop 2: 48% WBTC → 24% USDC borrow → ~23% WBTC
- Loop 3: 23% WBTC → final collateral (no borrow)
Final: ~1.71 WBTC collateral, ~0.74 BTC worth of USDC debt
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 burnedAutomatic 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%)
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
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
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:726Leaves 2 microUSDC buffer to avoid Vesu rounding errors.
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
- Liquidation Risk: If BTC drops >30% rapidly, USDC debt could exceed 75% of collateral value → liquidation
- Negative Yield Risk: If USDC borrow APY > WBTC supply APY, vault loses money
- Swap Slippage: Each loop loses ~2% to USDC→WBTC swap fees (Ekubo 0.05% + slippage)
- Flash Loan Dependency: Withdrawals require Vesu flash loans (zero fee, but dependency)
- Oracle Risk: Vesu’s Pragma price feed failure could cause incorrect borrows
- 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