Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/collinsville22/Sable/llms.txt

Use this file to discover all available pages before exploring further.

Sable Apex

The Apex vault is Sable’s most advanced strategy, combining three yield pillars to maximize diversified BTC returns.
Risk Level: 5 (High)
Strategy: 40% Vesu leverage loop + 35% Ekubo WBTC/ETH LP + 25% Endur xWBTC staking

Strategy Overview

WBTC Deposit (100%)

    ├── 40% → Vesu 3x Leverage Loop (same as Turbo)
    │         ↓
    │    Amplified Lending APY + BTCFi STRK

    ├── 35% → Ekubo WBTC/ETH Concentrated Liquidity LP
    │         ↓
    │    Swap Fees + Impermanent Gain/Loss

    └── 25% → Endur xWBTC Liquid Staking

         Validator Rewards + xWBTC Appreciation

Yield Sources

PillarAllocationYield TypeAPY (Example)
Vesu Leverage40%Lending APY (3x amplified) + BTCFi STRK12-18%
Ekubo LP35%Swap fees + IL8-15%
Endur Staking25%Validator rewards + xWBTC appreciation5-10%
Blended APY100%Weighted average9-15%
Impermanent Loss (IL): Ekubo LP position gains when ETH outperforms BTC, loses when BTC outperforms ETH. This creates natural hedging.

Risk Profile

  • High complexity: Three simultaneous strategies increase smart contract risk surface
  • Moderate leverage: 40% of deposit uses 3x loop (overall portfolio leverage ~1.3x)
  • IL exposure: Ekubo LP loses value if BTC/ETH ratio diverges significantly
  • Multi-protocol risk: Depends on Vesu, Ekubo, Endur, and AVNU all working correctly
  • Liquidity risk: Ekubo LP withdrawal depends on pool liquidity

Contract Architecture

File: apex.cairo (~1000 LOC)
Deployed: 0x071eb7fc3a912c0ee85b1dc795e29fd77ff4203a33384a1171dd4fcb7c7b3df9

Core Components

Apex auto-splits every deposit into three strategies:
fn _deploy_to_strategy(ref self: ContractState, amount: u256) {
    // Split: 40% Vesu, 35% Ekubo, 25% Endur
    let staking_amount = (amount * 25) / 100;
    let ekubo_amount = (amount * 35) / 100;
    let vesu_amount = amount - staking_amount - ekubo_amount;

    // Pillar 3: Stake 25% to Endur -> xWBTC
    let endur = IERC4626Dispatcher { contract_address: endur_addr };
    let xwbtc_shares = endur.deposit(staking_amount, this);
    self.endur_staked.write(self.endur_staked.read() + xwbtc_shares);

    // Pillar 2: Deploy 35% to Ekubo WBTC/ETH LP
    self._deploy_to_ekubo_lp(ekubo_amount);

    // Pillar 1: Deploy 40% to Vesu 3x leverage loop
    self._deploy_to_vesu_leverage(vesu_amount);
}
From apex.cairo:1000

Key Functions

User Functions

deposit
function
Deposit WBTC and receive yvBTC-APEX sharesParameters:
  • assets (u256): Amount of WBTC to deposit (8 decimals)
  • receiver (ContractAddress): Address to receive vault shares
Returns: u256 — Number of shares minted
Auto-splits deposit:
  • 25% → Endur xWBTC staking (instant)
  • 35% → Ekubo WBTC/ETH LP (swap half to ETH first)
  • 40% → Vesu 3x leverage loop (deposit → borrow → swap → repeat)
withdraw
function
Burn shares and withdraw WBTCParameters:
  • assets (u256): Amount of WBTC to withdraw
  • receiver (ContractAddress): Address to receive WBTC
  • owner (ContractAddress): Share owner
Returns: u256 — Number of shares burned
Multi-Strategy Unwind:
  1. Withdraw from Ekubo LP (WBTC + ETH) → swap ETH → WBTC
  2. Swap xWBTC → WBTC via AVNU (Endur has 7-day queue)
  3. Flash loan unwind Vesu leverage (if debt exists)
  4. Transfer combined WBTC to user

Curator Functions (Pillar 1: Vesu Leverage)

execute_leverage
function
Execute one Vesu leverage loop (deposit WBTC → borrow USDC → swap → re-deposit)Parameters:
  • collateral_amount (u256): WBTC to deposit
  • borrow_amount (u256): USDC to borrow
  • min_swap_out (u256): Minimum WBTC from USDC swap
  • routes (Array<Route>): AVNU swap routes
fn execute_leverage(
    ref self: ContractState,
    collateral_amount: u256,
    borrow_amount: u256,
    min_swap_out: u256,
    routes: Array<Route>,
)
Implementation at apex.cairo:379
deleverage
function
Reduce Vesu leverage (repay USDC debt + withdraw WBTC 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 apex.cairo:449
flash_unwind_vesu
function
Atomically close all Vesu 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_vesu(ref self: ContractState, wbtc_to_sell: u256, min_usdc_out: u256, routes: Array<Route>)
Implementation at apex.cairo:668

Curator Functions (Pillar 2: Ekubo LP)

deploy_to_ekubo
function
Deploy WBTC to Ekubo WBTC/ETH concentrated liquidity LPParameters:
  • wbtc_amount (u256): WBTC to deploy
  • eth_min_out (u256): Minimum ETH from half WBTC swap
  • routes (Array<Route>): AVNU WBTC→ETH swap routes
  • pool_key (PoolKey): Ekubo pool identifier (token0, token1, fee, tick_spacing, extension)
  • bounds (Bounds): LP tick range (lower, upper)
  • min_liquidity (u128): Minimum liquidity to mint (slippage protection)
fn deploy_to_ekubo(
    ref self: ContractState,
    wbtc_amount: u256,
    eth_min_out: u256,
    routes: Array<Route>,
    pool_key: PoolKey,
    bounds: Bounds,
    min_liquidity: u128,
)
Implementation at apex.cairo:501
Ekubo Pattern: Transfers tokens to Positions contract, then calls mint_and_deposit or deposit. Positions reads balanceOf(positions_address) internally.
withdraw_from_ekubo
function
Withdraw liquidity from Ekubo LP positionParameters:
  • liquidity (u128): Liquidity units to withdraw
  • pool_key (PoolKey): Same as used in deploy_to_ekubo
  • bounds (Bounds): Same tick range
  • min_token0 (u128): Minimum WBTC output (slippage protection)
  • min_token1 (u128): Minimum ETH output
fn withdraw_from_ekubo(
    ref self: ContractState,
    liquidity: u128,
    pool_key: PoolKey,
    bounds: Bounds,
    min_token0: u128,
    min_token1: u128,
)
Implementation at apex.cairo:563Returns WBTC + ETH to vault. Curator must swap ETH → WBTC separately if needed.

Curator Functions (Pillar 3: Endur Staking)

stake_to_endur
function
Stake idle WBTC into Endur vaultParameters:
  • amount (u256): WBTC to stake
fn stake_to_endur(ref self: ContractState, amount: u256)
Implementation at apex.cairo:600
unwind_endur
function
Swap all idle xWBTC → WBTC via AVNU (bypasses 7-day Endur queue)Parameters:
  • min_amount_out (u256): Minimum WBTC from xWBTC swap
  • routes (Array<Route>): AVNU xWBTC→WBTC swap routes
fn unwind_endur(ref self: ContractState, min_amount_out: u256, routes: Array<Route>)
Implementation at apex.cairo:691

Integration with External Protocols

Vesu PRIME Pool (Pillar 1)

Pool ID: 0x0451fe483d5921a2919ddd81d0de6696669bccdacd859f72a4fba7656b97c3b5 Apex uses Vesu PRIME for leverage loops (same as Turbo vault):
  • Collateral: WBTC
  • Debt: USDC
  • Strategy: 3x loop (deposit → borrow → swap → repeat)

Ekubo WBTC/ETH Pool (Pillar 2)

Pool Key:
PoolKey {
    token0: wbtc_addr,  // WBTC (lower address)
    token1: eth_addr,   // ETH (0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7)
    fee: 0x20c49ba5e353f80000000000000000,  // 0.05%
    tick_spacing: 1000,
    extension: Zero::zero(),
}
Apex provides concentrated liquidity (narrow tick range) to maximize fee capture. Ekubo Contracts:
  • Core: 0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b
  • Positions (NFT): ekubo_positions (constructor parameter)
LP Valuation: Apex reads live LP value via get_token_info:
let info = positions.get_token_info(position_id, pool_key, bounds);
// info.amount0 = WBTC principal
// info.fees0 = WBTC swap fees earned
// info.amount1 = ETH principal
// info.fees1 = ETH swap fees earned
// info.pool_price.sqrt_ratio = current pool price (for ETH→WBTC conversion)
From apex.cairo:936

Endur xWBTC Vault (Pillar 3)

Address: endur_vault (constructor parameter) Apex stakes WBTC via Endur’s ERC-4626 vault:
let endur = IERC4626Dispatcher { contract_address: endur_addr };
let xwbtc_shares = endur.deposit(wbtc_amount, vault_address);
xWBTC share price increases over time as staking rewards accrue.

AVNU Swap Routes

Apex uses AVNU for all swaps:
  1. USDC → WBTC (Vesu leverage loop)
  2. WBTC → ETH (Ekubo LP deploy)
  3. ETH → WBTC (Ekubo LP unwind)
  4. xWBTC → WBTC (Endur unwind)
  5. WBTC → USDC (Flash loan repayment)

Example Usage

Depositing WBTC

import { Contract } from 'starknet'

const apexVault = new Contract(apexABI, apexAddress, account)
const wbtc = new Contract(erc20ABI, wbtcAddress, account)

// Check minimum deposit
const minDeposit = await apexVault.min_deposit()
console.log('Min deposit:', minDeposit / 1e8, 'WBTC')

// Deposit (auto-splits 40/35/25)
const amount = 200_000_000n // 2.0 WBTC
await wbtc.approve(apexAddress, amount)
await apexVault.deposit(amount, account.address)

// Check yvBTC-APEX balance
const shares = await apexVault.balance_of(account.address)

Checking Three-Pillar Allocation

// Pillar 1: Vesu leverage (collateral, debt, loops, paused)
const [vesuColl, vesuDebt, numLoops, paused] = await apexVault.get_strategy_info()
console.log('Vesu collateral:', vesuColl / 1e8, 'WBTC')
console.log('Vesu debt:', vesuDebt / 1e6, 'USDC')

// Pillar 2: Ekubo LP (position_id, liquidity)
const [ekuboPositionId, ekuboLiquidity] = await apexVault.get_ekubo_position()
console.log('Ekubo position ID:', ekuboPositionId)
console.log('Ekubo liquidity:', ekuboLiquidity)

// Pillar 3: Endur staking (xWBTC shares)
const endurStaked = await apexVault.get_endur_staked()
console.log('Endur xWBTC shares:', endurStaked / 1e8)

// Convert to WBTC values
const endurVault = new Contract(erc4626ABI, endurAddress, provider)
const endurWBTCValue = await endurVault.convert_to_assets(endurStaked)

const vesuNetValue = vesuColl - (vesuDebt * 95000n / 1e6) // Assume $95k/BTC

console.log('\nPillar Values (WBTC):')
console.log('Vesu (net):', vesuNetValue / 1e8)
console.log('Ekubo LP: [read from get_token_info]')
console.log('Endur:', endurWBTCValue / 1e8)

Reading Ekubo LP Live Value

const ekuboPositions = new Contract(ekuboPositionsABI, ekuboPositionsAddress, provider)

// Get pool key and bounds from vault
const poolKey = {
  token0: wbtcAddress,
  token1: ethAddress,
  fee: 0x20c49ba5e353f80000000000000000n,
  tick_spacing: 1000,
  extension: 0n,
}
const bounds = {
  lower: { mag: lowerMag, sign: lowerSign },
  upper: { mag: upperMag, sign: upperSign },
}

// Read live LP info
const info = await ekuboPositions.get_token_info(ekuboPositionId, poolKey, bounds)
console.log('WBTC principal:', info.amount0 / 1e8)
console.log('WBTC fees:', info.fees0 / 1e8)
console.log('ETH principal:', info.amount1 / 1e18)
console.log('ETH fees:', info.fees1 / 1e18)
console.log('Pool sqrt_ratio:', info.pool_price.sqrt_ratio)

// Convert ETH to WBTC equivalent
const wad = 1e18
const two_pow_128 = 2n ** 128n
const invSqrtRatio = (two_pow_128 * BigInt(wad)) / info.pool_price.sqrt_ratio
const ethTotal = info.amount1 + info.fees1
const ethInWBTC = (ethTotal * invSqrtRatio / BigInt(wad)) * invSqrtRatio / BigInt(wad)
const totalWBTC = info.amount0 + info.fees0 + ethInWBTC
console.log('Total LP value (WBTC):', totalWBTC / 1e8)

Security Considerations

Risks

  1. Multi-Protocol Risk: Depends on 4 protocols (Vesu, Ekubo, Endur, AVNU) — any failure impacts vault
  2. Impermanent Loss: Ekubo LP loses value if BTC/ETH ratio changes significantly
  3. Liquidation Risk: Vesu leverage (40%) can be liquidated if BTC drops >30%
  4. Complexity Risk: 1000 LOC with three simultaneous strategies increases bug surface
  5. Ekubo Liquidity Risk: Large LP withdrawals may face slippage if pool is thin
  6. Gas Cost: Three-strategy deployment is expensive (~3x normal vault gas)

Risk Mitigation

  • Diversification: Three uncorrelated yield sources reduce single-point-of-failure risk
  • Conservative Vesu allocation: Only 40% uses leverage (not 100%)
  • Narrow Ekubo range: Concentrated liquidity maximizes fees, limits IL exposure
  • xWBTC swap escape: AVNU bypass for Endur’s 7-day withdrawal queue
  • Pause mechanism: Owner can halt deposits if any pillar fails
  • Live valuation: total_assets reads on-chain state from all three protocols

Impermanent Loss Monitoring

// Track IL over time
const initialBTCPrice = 95000
const initialETHPrice = 3500
const initialRatio = initialBTCPrice / initialETHPrice  // ~27.14

const currentBTCPrice = 100000
const currentETHPrice = 4000
const currentRatio = currentBTCPrice / currentETHPrice  // 25.0

// IL formula: IL = 2 * sqrt(price_ratio_new / price_ratio_old) / (1 + price_ratio_new / price_ratio_old) - 1
const priceChange = currentRatio / initialRatio  // 0.921
const IL = 2 * Math.sqrt(priceChange) / (1 + priceChange) - 1
console.log('Impermanent Loss:', (IL * 100).toFixed(2) + '%')  // -0.81%

// If ETH outperformed (ratio decreased), IL is negative (loss)
// If BTC outperformed (ratio increased), IL is also negative (loss)
// Only 0% IL when ratio stays constant

Additional Resources