0x036ce338c12e315b7b888020469e675c011ca5b9f6c5b657c4e6c00f33c03be7
0x0514f13dbfabf6ec1b73524ca47aa4d2b8d8d62b20c9ed6131566bc43d93d4fc
#[starknet::interface]
pub trait IBitcoinCollateralPerps<TContractState> {
// Collateral Management
fn deposit_btc_collateral(ref self: TContractState, amount: u256);
fn withdraw_btc_collateral(ref self: TContractState, amount: u256);
fn get_collateral_balance(self: @TContractState, user: ContractAddress) -> u256;
// Position Management
fn open_perp_position(
ref self: TContractState,
market_id: felt252,
size: u256,
is_long: bool,
leverage: u8
) -> felt252;
fn close_perp_position(ref self: TContractState, position_id: felt252);
fn get_position(self: @TContractState, position_id: felt252) -> Position;
// Risk Management
fn check_liquidation_health(self: @TContractState, position_id: felt252) -> bool;
// Admin Functions
fn set_extended_router(ref self: TContractState, router_address: ContractAddress);
fn set_oracle(ref self: TContractState, oracle_address: ContractAddress);
fn pause_contract(ref self: TContractState);
fn unpause_contract(ref self: TContractState);
// Extended Protocol Integration
fn fulfill_open_position(
ref self: TContractState,
request_id: felt252,
extended_order_id: felt252,
execution_price: u256
);
}Deposit tBTC tokens as collateral.
Parameters:
amount: u256- Amount in satoshis (1 tBTC = 100,000,000 satoshis)
Requirements:
- Amount >= 100,000,000 (1.0 tBTC minimum)
- User must approve tBTC token spending first
- Contract must not be paused
Events Emitted:
CollateralDeposited(user, amount, timestamp)
Example:
import { Contract, cairo } from 'starknet';
// 1. Approve tBTC spending
const tbtcToken = new Contract(erc20ABI, TBTC_TOKEN_ADDRESS, account);
await tbtcToken.approve(CONTRACT_ADDRESS, cairo.uint256(amount));
// 2. Deposit collateral
const contract = new Contract(contractABI, CONTRACT_ADDRESS, account);
const tx = await contract.deposit_btc_collateral(cairo.uint256(amount));
await provider.waitForTransaction(tx.transaction_hash);Withdraw available tBTC collateral.
Parameters:
amount: u256- Amount to withdraw in satoshis
Requirements:
- Amount <= available_collateral
- Contract must not be paused
Events Emitted:
CollateralWithdrawn(user, amount, timestamp)
Example:
const tx = await contract.withdraw_btc_collateral(cairo.uint256(amount));
await provider.waitForTransaction(tx.transaction_hash);Get user's available collateral balance.
Parameters:
user: ContractAddress- User's Starknet address
Returns:
u256- Available collateral in satoshis
Example:
const balance = await contract.get_collateral_balance(userAddress);
console.log('Balance:', balance.toString(), 'satoshis');
console.log('Balance:', (Number(balance) / 1e8).toFixed(8), 'tBTC');Open a new perpetual position.
Parameters:
market_id: felt252- Market identifier (e.g., 'BTC/USD', 'ETH/USD')size: u256- Position size in USD (with 8 decimals)is_long: bool- true for long, false for shortleverage: u8- Leverage multiplier (1-20)
Returns:
felt252- Position ID
Requirements:
- Sufficient available collateral
- Leverage <= 20
- Contract must not be paused
Events Emitted:
PositionOpened(position_id, user, market_id, size, is_long)ExtendedRequestCreated(request_id, position_id, user, market, size, is_long, request_type)
Example:
// Open a long BTC/USD position with 10x leverage
const marketId = stringToFelt252('BTC/USD');
const size = cairo.uint256(1000 * 1e8); // $1000 USD
const isLong = true;
const leverage = 10;
const tx = await contract.open_perp_position(
marketId,
size,
isLong,
leverage
);
const receipt = await provider.waitForTransaction(tx.transaction_hash);
// Extract position ID from events
const positionOpenedEvent = receipt.events.find(
e => e.keys[0] === 'PositionOpened'
);
const positionId = positionOpenedEvent.data[0];Close an existing position.
Parameters:
position_id: felt252- Position ID to close
Requirements:
- Position must exist and be active
- Caller must be position owner
- Contract must not be paused
Events Emitted:
PositionClosed(position_id, user)
Example:
const tx = await contract.close_perp_position(positionId);
await provider.waitForTransaction(tx.transaction_hash);Get position details.
Parameters:
position_id: felt252- Position ID
Returns:
Position- Position struct with all details
Example:
const position = await contract.get_position(positionId);
console.log('Position:', {
id: position.id,
owner: position.owner,
market_id: position.market_id,
size: position.size.toString(),
collateral: position.collateral.toString(),
entry_price: position.entry_price.toString(),
is_long: position.is_long,
leverage: position.leverage,
is_active: position.is_active
});Check if a position is healthy or at risk of liquidation.
Parameters:
position_id: felt252- Position ID
Returns:
bool- true if healthy, false if at risk
Example:
const isHealthy = await contract.check_liquidation_health(positionId);
if (!isHealthy) {
console.warn('Position at risk of liquidation!');
}import { PaymasterRpc } from 'starknet';
const paymasterRpc = new PaymasterRpc({
nodeUrl: 'https://sepolia.paymaster.avnu.fi',
headers: { 'x-paymaster-api-key': AVNU_API_KEY }
});Status: ✅ PRODUCTION READY
// 1. Get injected wallet account
const starknet = await connect();
await starknet.enable();
const injectedAccount = starknet.account;
// 2. Update provider to PaymasterRpc
(injectedAccount as any).provider = paymasterRpc;
// 3. Execute transaction with fee token
const result = await injectedAccount.execute(calls, {
feeToken: 'USDC' // or 'ETH', 'STRK'
});Supported Fee Tokens:
USDC- USD CoinETH- EthereumSTRK- Starknet Token
Key Points:
- ✅ Use injected wallet account (don't create Account manually)
- ✅ Works with ArgentX and Braavos v1.1.0+
- ✅ Supports SNIP-9 v2 (execute_from_outside)
- ✅ Real-time balance checking before transaction
https://api.starknet.sepolia.extended.exchange/api/v1
wss://api.starknet.sepolia.extended.exchange/stream.extended.exchange/v1
Endpoint: GET /markets
Response:
interface ExtendedMarketStats {
market_id: string;
symbol: string;
last_price: number;
change_24h: number;
volume_24h: number;
high_24h: number;
low_24h: number;
open_interest: number;
}Example:
const response = await fetch(
'https://api.starknet.sepolia.extended.exchange/api/v1/markets',
{
headers: {
'X-Api-Key': EXTENDED_API_KEY,
'Content-Type': 'application/json'
}
}
);
const markets = await response.json();Endpoint: GET /markets/{marketId}
Example:
const marketId = 'BTC-USD';
const response = await fetch(
`https://api.starknet.sepolia.extended.exchange/api/v1/markets/${marketId}`,
{
headers: {
'X-Api-Key': EXTENDED_API_KEY,
'Content-Type': 'application/json'
}
}
);
const stats = await response.json();Endpoint: POST /orders
Request:
interface ExtendedOrderRequest {
market_id: string;
side: 'long' | 'short';
size: string;
leverage: number;
order_type: 'market' | 'limit';
price?: string; // Required for limit orders
}Response:
interface ExtendedOrderResponse {
order_id: string;
status: 'pending' | 'filled' | 'rejected';
execution_price?: string;
timestamp: number;
}Endpoint: GET /positions/{address}
Response:
interface ExtendedPosition {
position_id: string;
market_id: string;
side: 'long' | 'short';
size: string;
entry_price: string;
current_price: string;
pnl: string;
leverage: number;
liquidation_price: string;
}Located in frontend/lib/contract.ts
async function getTBTCBalance(address: string): Promise<bigint>Get user's tBTC token balance.
async function approveTBTC(account: Account, amount: bigint): Promise<string>Approve tBTC token spending.
async function depositCollateral(
account: Account,
amount: bigint,
usePaymaster: boolean = false,
gasToken?: string
): Promise<string>Deposit tBTC collateral with optional AVNU Paymaster.
async function withdrawCollateral(
account: Account,
amount: bigint
): Promise<string>Withdraw tBTC collateral.
async function openPosition(
account: Account,
marketId: string,
size: bigint,
isLong: boolean,
leverage: number,
usePaymaster: boolean = false,
gasToken?: string
): Promise<string>Open a new position with optional AVNU Paymaster.
async function closePosition(
account: Account,
positionId: string
): Promise<string>Close an existing position.
async function getUserPositions(userAddress: string): Promise<Position[]>Get all user positions.
import { connect } from '@starknet-io/get-starknet';
import { cairo } from 'starknet';
import {
getTBTCBalance,
approveTBTC,
depositCollateral,
openPosition
} from './lib/contract';
// 1. Connect wallet
const starknet = await connect();
await starknet.enable();
const account = starknet.account;
// 2. Check tBTC balance
const balance = await getTBTCBalance(account.address);
console.log('tBTC Balance:', (Number(balance) / 1e8).toFixed(8));
// 3. Approve tBTC spending
const amount = BigInt(2 * 1e8); // 2.0 tBTC
const approveTx = await approveTBTC(account, amount);
console.log('Approve TX:', approveTx);
// 4. Deposit collateral with AVNU Paymaster (pay with USDC)
const depositTx = await depositCollateral(
account,
amount,
true, // usePaymaster
'USDC' // gasToken
);
console.log('Deposit TX:', depositTx);
// 5. Open position
const positionTx = await openPosition(
account,
'BTC/USD',
BigInt(1000 * 1e8), // $1000 USD
true, // long
10, // 10x leverage
true, // usePaymaster
'USDC' // gasToken
);
console.log('Position TX:', positionTx);
// 6. View on Starkscan
console.log(`https://sepolia.starkscan.co/tx/${positionTx}`);| Error Message | Cause | Solution |
|---|---|---|
Contract paused |
Contract is in paused state | Wait for unpause or contact admin |
Amount below minimum |
Deposit < 1.0 tBTC | Increase deposit amount |
Insufficient collateral |
Not enough available collateral | Deposit more tBTC |
tBTC transfer failed |
Approval or balance issue | Check approval and balance |
Position not found |
Invalid position ID | Verify position ID |
Not position owner |
Caller doesn't own position | Use correct wallet |
Leverage too high |
Leverage > 20 | Reduce leverage |
- Extended Protocol API: 100 requests/minute
- AVNU Paymaster: No documented limits
- Starknet RPC: Depends on provider
- Contract: Starkscan
- tBTC Token: Starkscan
- AVNU Docs: https://docs.avnu.fi/paymaster
- Extended Docs: https://docs.extended.exchange/