@aspan/sdk
v0.5.3
Published
TypeScript SDK for Aspan Protocol - LST-backed stablecoin on BNB Chain
Maintainers
Readme
@aspan/sdk
TypeScript SDK for Aspan Protocol - LST-backed stablecoin on BNB Chain.
Installation
npm install @aspan/sdk
# or
pnpm add @aspan/sdkContract Addresses (BSC Mainnet)
import { BSC_ADDRESSES } from "@aspan/sdk";
// Or use directly:
// BSC_ADDRESSES.diamond, BSC_ADDRESSES.router, etc.| Contract | Address |
|----------|---------|
| Diamond (Main Entry) | 0x6a11B30d3a70727d5477D6d8090e144443fA1c78 |
| Router | 0xb8a90CD2811d6DDbB4B7969d30B036574842cb6E |
| ApUSD | 0x4570047eeB5aDb4081c5d470494EB5134e34A287 |
| XBNB | 0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43 |
| SApUSD | 0xE2BE739C4aA4126ee72D612d9548C38B1B0e5A1b |
| wclisBNB | 0xb2A0631bF0aC326fEefc201E7337E13C63Bbed07 |
Quick Start
Read-Only Client (Query Data)
import { createAspanReadClient, formatAmount, formatCR, BSC_ADDRESSES } from "@aspan/sdk";
const client = createAspanReadClient(BSC_ADDRESSES.diamond, "https://bsc-dataseed.binance.org/");
const stats = await client.getProtocolStats();
console.log("TVL:", formatAmount(stats.tvlInUSD), "USD");
console.log("CR:", formatCR(stats.collateralRatio));Write Client - Browser (React/wagmi)
import { AspanClient, BSC_ADDRESSES } from "@aspan/sdk";
import { useWalletClient } from "wagmi";
function MyComponent() {
const { data: walletClient } = useWalletClient();
const getClient = () => {
if (!walletClient) return null;
return new AspanClient({
diamondAddress: BSC_ADDRESSES.diamond,
walletClient: walletClient,
});
};
const handleMint = async () => {
const client = getClient();
if (!client) return;
const hash = await client.mintApUSD({
lstToken: BSC_ADDRESSES.slisBNB,
lstAmount: parseAmount("1"),
});
console.log("Tx hash:", hash);
};
}Write Client - Node.js / Server
import { createAspanClient, parseAmount, BSC_ADDRESSES } from "@aspan/sdk";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0x...");
const client = createAspanClient(BSC_ADDRESSES.diamond, account);
const hash = await client.mintApUSD({
lstToken: BSC_ADDRESSES.slisBNB,
lstAmount: parseAmount("1"),
});Router (One-Click Operations)
AspanRouter is a periphery contract that enables users to mint/redeem apUSD or xBNB in a single transaction using common tokens like USDT, USDC, BNB, or WBNB. The router automatically handles DEX swaps, LST staking, and minting.
Router Client Setup
import { createRouterClient, AspanRouterClient, BSC_ADDRESSES } from "@aspan/sdk";
import { privateKeyToAccount } from "viem/accounts";
// Node.js
const account = privateKeyToAccount("0x...");
const router = createRouterClient(BSC_ADDRESSES.router, account);
// Browser (wagmi)
const router = new AspanRouterClient({
routerAddress: BSC_ADDRESSES.router,
walletClient, // from useWalletClient()
});Swap USDT/USDC → apUSD or xBNB (One-Click)
import { parseAmount, BSC_ADDRESSES, type SwapAndMintParams } from "@aspan/sdk";
import { zeroAddress } from "viem";
// Full control with SwapParams + MintParams
const params: SwapAndMintParams = {
swapParams: {
inputToken: BSC_ADDRESSES.USDT,
inputAmount: parseAmount("100"), // 100 USDT
targetLST: zeroAddress, // Use default LST
minLSTOut: 0n, // Or set slippage protection
poolFee: 2500, // 0.25% V3 pool fee
},
mintParams: {
mintXBNB: false, // false = apUSD, true = xBNB
minMintOut: parseAmount("99"), // Min 99 apUSD (1% slippage)
recipient: zeroAddress, // Send to msg.sender
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
},
};
// Approve USDT first, then swap+mint
const hash = await router.swapAndMint(params);
await router.waitForTransaction(hash);Simplified: Swap → apUSD/xBNB (Default LST)
// Simpler API using default LST
const hash = await router.swapAndMintDefault({
inputToken: BSC_ADDRESSES.USDT,
inputAmount: parseAmount("100"),
mintXBNB: false, // false = apUSD, true = xBNB
minMintOut: parseAmount("99"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
});Stake Native BNB → apUSD/xBNB (Bypasses DEX)
Direct BNB staking gets better rates for large amounts by bypassing DEX.
// Stake BNB directly to Lista/Astherus → mint apUSD or xBNB
const hash = await router.stakeAndMint({
targetLST: BSC_ADDRESSES.slisBNB,
isXBNB: false, // false = apUSD, true = xBNB
minMintOut: parseAmount("99"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
value: parseAmount("1"), // 1 BNB
});Direct Mint/Redeem via Router
If you already have LST, use direct functions:
// Mint apUSD or xBNB with LST
const hash = await router.mint({
lst: BSC_ADDRESSES.slisBNB,
lstAmount: parseAmount("10"),
mintXBNB: false, // false = apUSD, true = xBNB
minOut: parseAmount("99"),
});
// Redeem apUSD or xBNB for LST
const hash = await router.redeem({
lst: BSC_ADDRESSES.slisBNB,
redeemXBNB: false, // false = redeem apUSD, true = redeem xBNB
amount: parseAmount("100"),
minOut: parseAmount("0.9"),
});Redeem + Swap to USDT/BNB (V3 Path)
import { encodeV3Path, BSC_ADDRESSES } from "@aspan/sdk";
// Encode V3 swap path: slisBNB → WBNB → USDT
const path = encodeV3Path(
[BSC_ADDRESSES.slisBNB, BSC_ADDRESSES.WBNB, BSC_ADDRESSES.USDT],
[500, 500] // pool fees (0.05% each hop)
);
// Redeem apUSD/xBNB and swap LST to output token via V3 path
const hash = await router.redeemAndSwap({
lst: BSC_ADDRESSES.slisBNB,
redeemXBNB: false, // false = redeem apUSD, true = redeem xBNB
amount: parseAmount("100"),
path,
minOut: parseAmount("99"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
unwrapBNB: false, // true to unwrap WBNB → native BNB
});Get Native BNB (Auto-unwrap)
// Redeem and get native BNB (auto-unwrap WBNB)
const path = encodeV3Path(
[BSC_ADDRESSES.slisBNB, BSC_ADDRESSES.WBNB],
[2500] // 0.25% pool fee
);
const hash = await router.redeemAndSwap({
lst: BSC_ADDRESSES.slisBNB,
redeemXBNB: false,
amount: parseAmount("100"),
path,
minOut: parseAmount("0.15"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
unwrapBNB: true, // Auto-unwrap to native BNB!
});Native Unstake (7-15 Day Unbonding)
For no-slippage BNB redemption, use Lista's native unstake:
// Request unstake (starts 7-15 day unbonding period)
const hash = await router.redeemAndRequestUnstake({
redeemXBNB: false, // false = redeem apUSD, true = redeem xBNB
amount: parseAmount("100"),
});
const receipt = await router.waitForTransaction(hash);
// Check withdrawal status
const indices = await router.getUserWithdrawalIndices(account.address);
for (const idx of indices) {
const status = await router.getWithdrawalStatus(idx);
console.log(`Request ${idx}: claimable=${status.isClaimable}, amount=${status.bnbAmount}`);
}
// Claim BNB after unbonding
const claimHash = await router.claimUnstake(requestIndex);Router View Functions
// Check supported tokens
const defaultLST = await router.getDefaultLST();
const isSupported = await router.isSupportedInputToken(BSC_ADDRESSES.USDT);
const isLSTSupported = await router.isSupportedLST(BSC_ADDRESSES.slisBNB);
// Preview mint/redeem (accurate with fees)
const apUSDOut = await router.previewMint(BSC_ADDRESSES.slisBNB, parseAmount("1"), false);
const xBNBOut = await router.previewMint(BSC_ADDRESSES.slisBNB, parseAmount("1"), true);
const lstFromApUSD = await router.previewRedeem(BSC_ADDRESSES.slisBNB, false, parseAmount("100"));
const lstFromXBNB = await router.previewRedeem(BSC_ADDRESSES.slisBNB, true, parseAmount("10"));
// Get token addresses
const wbnb = await router.getWBNB();
const usdt = await router.getUSDT();
const slisBNB = await router.getSlisBNB();Use Cases
1. New User: USDT → apUSD (One-Click)
The simplest way to mint apUSD stablecoin with USDT.
import { createRouterClient, parseAmount, BSC_ADDRESSES } from "@aspan/sdk";
const router = createRouterClient(BSC_ADDRESSES.router, account);
// 100 USDT → apUSD (auto DEX swap + mint)
const hash = await router.swapAndMintDefault({
inputToken: BSC_ADDRESSES.USDT,
inputAmount: parseAmount("100"),
mintXBNB: false, // apUSD
minMintOut: parseAmount("99"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
});2. BNB Holder: BNB → apUSD/xBNB (Direct Stake)
For large BNB amounts, stake directly to Lista to avoid DEX slippage.
// 1 BNB → apUSD (direct stake, no DEX slippage)
const hash = await router.stakeAndMint({
targetLST: BSC_ADDRESSES.slisBNB,
isXBNB: false, // false = apUSD, true = xBNB
minMintOut: parseAmount("750"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
value: parseAmount("1"), // 1 BNB
});3. Leverage Trader: BNB → xBNB (Long BNB)
xBNB is leveraged BNB exposure, ideal for bullish BNB traders.
// 1 BNB → xBNB (~1.2x leverage)
const hash = await router.stakeAndMint({
targetLST: BSC_ADDRESSES.slisBNB,
isXBNB: true, // xBNB
minMintOut: parseAmount("0.001"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
value: parseAmount("1"),
});
// Check current leverage
const stats = await client.getProtocolStats();
console.log("xBNB Leverage:", formatAmount(stats.effectiveLeverage), "x");4. Yield Farming: apUSD → sApUSD (Stability Pool)
Deposit apUSD into the Stability Pool to earn protocol yield.
import { createAspanClient, parseAmount, BSC_ADDRESSES } from "@aspan/sdk";
const client = createAspanClient(BSC_ADDRESSES.diamond, account);
// Deposit apUSD
const hash = await client.deposit({ apUSDAmount: parseAmount("1000") });
// Check earnings
const position = await client.getUserStabilityPoolPosition(account.address);
console.log("sApUSD Balance:", formatAmount(position.balance));
console.log("Earned:", formatAmount(position.balance - position.deposited));
// Preview withdrawal (supports dirty pools with apUSD + xBNB)
const preview = await client.previewRedeemMulti(position.shares);
console.log("Will receive apUSD:", formatAmount(preview.apUSD));
if (preview.hasXBNB) {
console.log("Will also receive xBNB:", formatAmount(preview.xBNB));
}5. Quick Exit: apUSD/xBNB → USDT
Exit to USDT via DEX swap when you need liquidity fast.
import { encodeV3Path, BSC_ADDRESSES } from "@aspan/sdk";
// Path: slisBNB → WBNB → USDT (PancakeSwap V3)
const path = encodeV3Path(
[BSC_ADDRESSES.slisBNB, BSC_ADDRESSES.WBNB, BSC_ADDRESSES.USDT],
[500, 500]
);
// 100 apUSD → USDT
const hash = await router.redeemAndSwap({
lst: BSC_ADDRESSES.slisBNB,
redeemXBNB: false, // apUSD
amount: parseAmount("100"),
path,
minOut: parseAmount("99"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
unwrapBNB: false,
});6. Quick Exit: apUSD/xBNB → Native BNB (Lista StableSwap)
Exit to native BNB using Lista StableSwap for better rates on slisBNB → WBNB.
// For slisBNB → WBNB, Router auto-routes through Lista StableSwap
// Just use a single-hop path
const path = encodeV3Path(
[BSC_ADDRESSES.slisBNB, BSC_ADDRESSES.WBNB],
[100] // Fee tier ignored for Lista StableSwap route
);
// xBNB → Native BNB
const hash = await router.redeemAndSwap({
lst: BSC_ADDRESSES.slisBNB,
redeemXBNB: true, // xBNB
amount: parseAmount("0.001"),
path,
minOut: parseAmount("0.8"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
unwrapBNB: true, // Auto-unwrap WBNB to native BNB
});7. Zero-Slippage Exit: Lista Native Unstake (7 Days)
Don't want to pay DEX slippage? Use Lista native unstake for 1:1 BNB redemption after 7 days.
// Step 1: Request unstake
const hash = await router.redeemAndRequestUnstake({
redeemXBNB: false, // apUSD
amount: parseAmount("100"),
});
await router.waitForTransaction(hash);
// Step 2: Check status
const indices = await router.getUserWithdrawalIndices(account.address);
for (const idx of indices) {
const status = await router.getWithdrawalStatus(idx);
console.log(`#${idx}: ${status.isClaimable ? "Claimable" : "Pending"}, ${formatAmount(status.bnbAmount)} BNB`);
}
// Step 3: Claim BNB after 7 days
const claimHash = await router.claimUnstake(requestIndex);User Flows
Flow 1: Mint apUSD (Stablecoin)
User deposits LST (e.g., slisBNB) to mint apUSD stablecoin.
import { createAspanClient, parseAmount, BSC_ADDRESSES } from "@aspan/sdk";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0x...");
const client = createAspanClient(BSC_ADDRESSES.diamond, account);
const lstAmount = parseAmount("10"); // 10 slisBNB
// Check fees
const fees = await client.getCurrentFees();
if (fees.apUSDMintDisabled) throw new Error("Minting disabled in current CR");
// Approve LST, then mint with slippage protection
const minOut = lstAmount * 995n / 1000n; // 0.5% slippage tolerance
const hash = await client.mintApUSD({
lstToken: BSC_ADDRESSES.slisBNB,
lstAmount,
minOut,
});
await client.waitForTransaction(hash);Flow 2: Redeem apUSD for LST
const apUSDAmount = parseAmount("5000");
// Calculate expected LST and set minOut
const lstPrice = await client.getLSTPriceUSD(BSC_ADDRESSES.slisBNB);
const expectedLST = (apUSDAmount * parseAmount("1")) / lstPrice;
const minOut = expectedLST * 995n / 1000n;
const hash = await client.redeemApUSD({
lstToken: BSC_ADDRESSES.slisBNB,
apUSDAmount,
minOut,
});Flow 3: Stake apUSD to Earn Yield (sApUSD)
// Check pool stats
const poolStats = await client.getStabilityPoolStats();
console.log("Exchange Rate:", formatAmount(poolStats.exchangeRate));
// Deposit apUSD
const hash = await client.deposit({ apUSDAmount: parseAmount("1000") });
// Check position
const position = await client.getUserStabilityPoolPosition(account.address);
console.log("Balance (incl. yield):", formatAmount(position.balance));Protocol Metrics
const stats = await client.getProtocolStats();
const fees = await client.getCurrentFees();
const mode = await client.getStabilityMode();
console.log("TVL:", formatAmount(stats.tvlInUSD), "USD");
console.log("CR:", formatCR(stats.collateralRatio));
console.log("Stability Mode:", mode.mode);
console.log("xBNB Price:", formatAmount(stats.xBNBPriceBNB), "BNB");
console.log("Leverage:", formatAmount(stats.effectiveLeverage), "x");API Reference
Router Write Functions (v2.0.0)
| Function | Description |
|----------|-------------|
| swapAndMint(params) | Swap input token → LST → mint apUSD/xBNB |
| swapAndMintDefault(params) | Simplified swap+mint using default LST |
| stakeAndMint(params) | Stake BNB directly → LST → mint apUSD/xBNB |
| mint(params) | Direct mint apUSD/xBNB with LST |
| redeem(params) | Direct redeem apUSD/xBNB for LST |
| redeemAndSwap(params) | Redeem + swap LST to output token (V3 path, optional unwrapBNB) |
| redeemAndRequestUnstake(params) | Redeem + request native unstake from Lista |
| claimUnstake(requestIndex) | Claim BNB after unbonding period |
Router View Functions
| Function | Description |
|----------|-------------|
| previewMint(lst, amount, mintXBNB) | Preview mint output |
| previewRedeem(lst, redeemXBNB, amount) | Preview redeem output |
| getDefaultLST() | Get default LST address |
| isSupportedInputToken(token) | Check if input token supported |
| isSupportedLST(lst) | Check if LST supported |
| getUserWithdrawalIndices(user) | Get user's unstake request indices |
| getWithdrawalStatus(index) | Check unstake request status |
Diamond Write Functions
| Function | Description |
|----------|-------------|
| mintApUSD(params) | Mint apUSD with LST |
| redeemApUSD(params) | Redeem apUSD for LST |
| mintXBNB(params) | Mint xBNB with LST |
| redeemXBNB(params) | Redeem xBNB for LST |
| deposit(params) | Deposit apUSD to stability pool |
| withdraw(params) | Withdraw from stability pool |
| previewRedeemMulti(shares) | Preview multi-asset withdrawal (apUSD + xBNB) |
| harvestYield() | Trigger yield distribution |
Utility Functions
import {
formatAmount, // bigint -> "1234.5678"
parseAmount, // "1234.5678" -> bigint
formatFeeBPS, // 25 -> "0.25%"
formatCR, // 15000n -> "150%"
formatPriceUSD, // 58325000000n -> "$583.25"
encodeV3Path, // Encode PancakeSwap V3 swap path
BSC_ADDRESSES, // All contract addresses
PRECISION, // 10^18
BPS_PRECISION, // 10000
PRICE_PRECISION, // 10^8
} from "@aspan/sdk";Testnet
import {
createAspanTestnetReadClient,
createAspanTestnetClient,
createRouterTestnetReadClient,
createRouterTestnetClient,
} from "@aspan/sdk";
const client = createAspanTestnetReadClient("0x...");
const router = createRouterTestnetClient("0x...", account);Risk Keeper Bot
The SDK includes a Risk Keeper service for automated risk management:
Features
- Stability Mode 2 Trigger - Automatically triggers SM2 when CR < 130%
- TWAP Vault Cleaning - Cleans xBNB from sApUSD vault after SM2
Environment Variables
# Required for Risk Keeper
RISK_KEEPER_ENABLED=true
KEEPER_PRIVATE_KEY=0x...
SAPUSD_ADDRESS=0xE2BE739C4aA4126ee72D612d9548C38B1B0e5A1b
# Optional (with defaults)
ROUTER_ADDRESS=0x10ED43C718714eb63d5aA57B78B54704E256024E # PancakeSwap V2
WBNB_ADDRESS=0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
SM2_COOLDOWN_MS=1800000 # 30 min
TWAP_INTERVAL_MS=600000 # 10 min
TWAP_PERCENTAGE=5 # 5% per batch
MAX_SLIPPAGE_BPS=50 # 0.5%
TWAP_START_CR=13500 # 135%Risk Management Functions
| Function | Description |
|----------|-------------|
| triggerStabilityMode2(targetCR) | Trigger SM2 forced conversion |
| cleanXbnb(amount) | Clean underwater xBNB (user burn) |
| canTriggerStabilityMode2() | Check if SM2 can be triggered |
| getStabilityMode() | Get current stability mode (0/1/2) |
License
MIT
