Skip to main content

Sable Sentinel

The Sentinel vault is the simplest and safest BTC yield strategy on Sable. It supplies WBTC directly to the Vesu PRIME lending pool, earning base lending APY plus BTCFi Season STRK rewards.
Risk Level: 1 (Low)
Strategy: Pure lending with no leverage, no debt, no swaps

Strategy Overview

Sentinel implements a single-step strategy:
WBTC Deposit → Vesu PRIME Pool → Lending Yield + STRK Rewards

Yield Sources

SourceTypeDescription
Vesu Lending APYBase yieldInterest from WBTC borrowers on Vesu PRIME
BTCFi STRK RewardsIncentive100M STRK distribution for BTC strategies

Risk Profile

  • No leverage: No borrowed positions or liquidation risk
  • No external dependencies: Single protocol integration (Vesu)
  • No price risk: No exposure to volatile assets like ETH or USDC
  • Smart contract risk only: Limited to Vesu PRIME pool audits

Contract Architecture

File: sentinel.cairo (~500 LOC)
Deployed: 0x04ec7fdb1679450fb88eae9facc439a46be4ddeba628211e269a7467f6e0971b

Core Components

Sentinel implements the ERC-4626 tokenized vault standard:
  • Share Token: yvBTC-SENT (8 decimals)
  • Asset: WBTC (8 decimals)
  • Share Price: Increases as Vesu lending yield accrues
fn total_assets(self: @ContractState) -> u256 {
    self.total_assets_managed.read()
}

fn convert_to_shares(self: @ContractState, assets: u256) -> u256 {
    let supply = self.total_supply.read();
    let total = self.total_assets_managed.read();
    if supply == 0 || total == 0 { assets } else { (assets * supply) / total }
}

Key Functions

User Functions

deposit
function
Deposit WBTC and receive yvBTC-SENT sharesParameters:
  • assets (u256): Amount of WBTC to deposit (8 decimals)
  • receiver (ContractAddress): Address to receive vault shares
Returns: u256 — Number of shares minted
fn deposit(ref self: ContractState, assets: u256, receiver: ContractAddress) -> u256
withdraw
function
Burn shares and withdraw WBTCParameters:
  • assets (u256): Amount of WBTC to withdraw
  • receiver (ContractAddress): Address to receive WBTC
  • owner (ContractAddress): Share owner (must approve if caller ≠ owner)
Returns: u256 — Number of shares burned
fn withdraw(ref self: ContractState, assets: u256, receiver: ContractAddress, owner: ContractAddress) -> u256
Withdrawal automatically unwinds Vesu positions if idle WBTC is insufficient.
total_assets
view
Get total WBTC managed by the vault (idle + deployed)
fn total_assets(self: @ContractState) -> u256
Value from sentinel.cairo:183

Curator Functions

These functions are owner-only and manage the vault’s Vesu strategy.
deploy_to_vesu
function
Deploy idle WBTC to Vesu PRIME pool as collateralParameters:
  • amount (u256): WBTC amount to deploy
fn deploy_to_vesu(ref self: ContractState, amount: u256)
Implementation at sentinel.cairo:351
withdraw_from_vesu
function
Withdraw WBTC collateral from Vesu back to vaultParameters:
  • amount (u256): WBTC amount to withdraw
fn withdraw_from_vesu(ref self: ContractState, amount: u256)
Implementation at sentinel.cairo:380
harvest
function
Refresh total_assets from Vesu on-chain position
fn harvest(ref self: ContractState)
Reads Vesu position value and updates total_assets_managed, increasing share price. Implementation at sentinel.cairo:409

Integration with Vesu

Vesu PRIME Pool

Pool ID: 0x0451fe483d5921a2919ddd81d0de6696669bccdacd859f72a4fba7656b97c3b5 Sentinel supplies WBTC to the Vesu PRIME lending pool and earns:
  1. Base APY: Interest from borrowers (variable rate)
  2. BTCFi Rewards: STRK token incentives from StarkNet’s 100M STRK BTCFi Season program

Position Accounting

fn _refresh_total_assets(ref self: ContractState) {
    let vesu = IVesuPoolDispatcher { contract_address: pool_addr };
    let (_position, collateral_value, _debt_value) = vesu.position(wbtc_addr, debt_asset, this);
    let idle = wbtc.balance_of(this);
    self.total_assets_managed.write(collateral_value + idle);
}
From sentinel.cairo:483

Example Usage

Depositing WBTC

import { Contract, Account } from 'starknet'

const sentinelVault = new Contract(sentinelABI, sentinelAddress, account)
const wbtc = new Contract(erc20ABI, wbtcAddress, account)

// 1. Approve Sentinel vault to spend WBTC
const amount = 100_000_000n // 1 WBTC (8 decimals)
await wbtc.approve(sentinelAddress, amount)

// 2. Deposit WBTC and receive yvBTC-SENT shares
const { transaction_hash } = await sentinelVault.deposit(amount, account.address)

Withdrawing WBTC

// Get current share balance
const shares = await sentinelVault.balance_of(account.address)

// Convert shares to WBTC amount
const wbtcOut = await sentinelVault.convert_to_assets(shares)

// Withdraw all WBTC (burns all shares)
await sentinelVault.redeem(shares, account.address, account.address)

Checking Vault Stats

// Total WBTC managed (idle + deployed)
const totalAssets = await sentinelVault.total_assets()

// Current share price (WBTC per share)
const totalSupply = await sentinelVault.total_supply()
const sharePrice = totalAssets / totalSupply

// Strategy info: (collateral, debt, loops, paused)
const [collateral, debt, loops, paused] = await sentinelVault.get_strategy_info()

Security Considerations

Smart Contract Risk: Sentinel’s safety depends on Vesu PRIME pool security

Risk Mitigation

  • No liquidation risk: Sentinel has no debt positions
  • No oracle risk: No price feeds required for strategy execution
  • Pause mechanism: Owner can pause deposits in emergencies
  • Upgradeable: Contract class can be replaced via upgrade(new_class_hash)

Audits

  • Vesu V2: Audit report
  • Sable Vaults: Internal review (BTCFi Hackathon submission)

Additional Resources

Build docs developers (and LLMs) love