Loading...
Loading...
Complete Sanctum SDK for liquid staking, LST swaps, and Infinity pool operations on Solana. Use when working with LSTs (mSOL, jitoSOL, bSOL, INF), staking SOL, swapping between liquid staking tokens, or integrating Sanctum's liquidity infrastructure.
npx skill4agent add sendaifun/skills sanctumconst SANCTUM_API_BASE = 'https://sanctum-api.ironforge.network';
// All endpoints require API key
const headers = {
'Content-Type': 'application/json',
};
// Example: Get all LST metadata
const response = await fetch(
`${SANCTUM_API_BASE}/lsts?apiKey=${API_KEY}`
);
const lsts = await response.json();const LST_MINTS = {
// Native SOL (wrapped)
SOL: 'So11111111111111111111111111111111111111112',
// Sanctum INF (Infinity pool token)
INF: '5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm',
// Major LSTs
mSOL: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So', // Marinade
jitoSOL: 'J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn', // Jito
bSOL: 'bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1', // BlazeStake
// Partner LSTs
jupSOL: 'jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v', // Jupiter
bbSOL: 'bbso1MfE7KVL7DhqwZ6dVfKrD3oNV1PEykLNM4kk5dD', // Bybit
dSOL: 'Dso1bDeDjCQxTrWHqUUi63oBvV7Mdm6WaobLbQ7gnPQ', // Drift
// Other popular LSTs
hSOL: 'he1iusmfkpAdwvxLNGV8Y1iSbj4rUy6yMhEA3fotn9A', // Helius
pwrSOL: 'pWrSoLAhue6jUxUMbWaY8izMhNpWfhiJk7M3Fy3p1Kt', // Power
laineSOL: 'LAinEtNLgpmCP9Rvsf5Hn8W6EhNiKLZQti1xfWMLy6X', // Laine
};// INF increases in value over time
// Example: 1 INF deposited at 1.0 SOL might be worth 1.05 SOL later
const infValue = await getInfSolValue(); // Returns current INF/SOL rate// Each LST has a dedicated calculator
const SOL_VALUE_CALCULATORS = {
SPL: 'sp1V4h2gWorkGhVcazBc22Hfo2f5sd7jcjT4EDPrWFF',
SanctumSpl: 'sspUE1vrh7xRoXxGsg7vR1zde2WdGtJRbyK9uRumBDy',
SanctumSplMulti: 'ssmbu3KZxgonUtjEMCKspZzxvUQCxAFnyh1rcHUeEDo',
Marinade: 'mare3SCyfZkAndpBRBeonETmkCCB3TJTTrz8ZN2dnhP',
Lido: '1idUSy4MGGKyKhvjSnGZ6Zc7Q4eKQcibym4BkEEw9KR',
wSOL: 'wsoGmxQLSvwWpuaidCApxN5kEowLe2HLQLJhCQnj4bE',
};https://sanctum-api.ironforge.networkapiKey// Get all LSTs
GET /lsts?apiKey={key}
// Get specific LST by mint or symbol
GET /lsts/{mintOrSymbol}?apiKey={key}
// Response
interface LstMetadata {
symbol: string;
mint: string;
decimals: number;
tokenProgram: string;
logoUri: string;
name: string;
tvl: number; // Total Value Locked
apy: number; // Current APY
holders: number;
}// Get validator APYs
GET /validators/apy?apiKey={key}
// Response
interface ValidatorApy {
[validatorId: string]: {
avgApy: number;
timeseries: Array<{epoch: number; apy: number}>;
};
}
// Get LST historical APYs
GET /lsts/{mintOrSymbol}/apys?apiKey={key}&limit={n}
// Response
interface LstApyHistory {
apys: Array<{
epoch: number;
epochEndTs: number;
apy: number;
}>;
}// Get swap quote and unsigned transaction
GET /swap/token/order?apiKey={key}&inp={inputMint}&out={outputMint}&amt={amount}&mode={ExactIn|ExactOut}&signer={walletPubkey}&slippageBps={bps}
// Response
interface SwapOrderResponse {
tx: string; // Base64 encoded transaction
inpAmt: string; // Input amount
outAmt: string; // Output amount
source: string; // Swap source (Infinity, Router, etc.)
feeAmt: string; // Fee amount
feeMint: string; // Fee token mint
}
// Execute signed swap
POST /swap/token/execute
Body: {
signedTx: string; // Base64 signed transaction
orderResponse: object; // Original order response
}
// Response
interface SwapExecuteResponse {
txSignature: string;
}// Convert native stake account to LST
GET /swap/depositStake/order?apiKey={key}&stakeAccount={pubkey}&outputLstMint={mint}&signer={wallet}
POST /swap/depositStake/execute
Body: {
signedTx: string;
orderResponse: object;
}// Convert LST back to stake account
GET /swap/withdrawStake/order?apiKey={key}&lstMint={mint}&amount={amount}&signer={wallet}&deactivate={boolean}
POST /swap/withdrawStake/execute
Body: {
signedTx: string;
orderResponse: object;
}import { Connection, PublicKey, Transaction, Keypair } from '@solana/web3.js';
class SanctumClient {
private apiKey: string;
private baseUrl = 'https://sanctum-api.ironforge.network';
private connection: Connection;
constructor(apiKey: string, connection: Connection) {
this.apiKey = apiKey;
this.connection = connection;
}
// Get all LSTs
async getLsts(): Promise<LstMetadata[]> {
const response = await fetch(
`${this.baseUrl}/lsts?apiKey=${this.apiKey}`
);
return response.json();
}
// Get specific LST
async getLst(mintOrSymbol: string): Promise<LstMetadata> {
const response = await fetch(
`${this.baseUrl}/lsts/${mintOrSymbol}?apiKey=${this.apiKey}`
);
return response.json();
}
// Get swap quote
async getSwapQuote(params: {
inputMint: string;
outputMint: string;
amount: string;
mode: 'ExactIn' | 'ExactOut';
signer: string;
slippageBps?: number;
}): Promise<SwapOrderResponse> {
const url = new URL(`${this.baseUrl}/swap/token/order`);
url.searchParams.set('apiKey', this.apiKey);
url.searchParams.set('inp', params.inputMint);
url.searchParams.set('out', params.outputMint);
url.searchParams.set('amt', params.amount);
url.searchParams.set('mode', params.mode);
url.searchParams.set('signer', params.signer);
if (params.slippageBps) {
url.searchParams.set('slippageBps', params.slippageBps.toString());
}
const response = await fetch(url.toString());
return response.json();
}
// Execute swap
async executeSwap(
orderResponse: SwapOrderResponse,
signer: Keypair
): Promise<string> {
// Decode and sign transaction
const txBuffer = Buffer.from(orderResponse.tx, 'base64');
const transaction = Transaction.from(txBuffer);
transaction.sign(signer);
// Send to API
const response = await fetch(`${this.baseUrl}/swap/token/execute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
signedTx: transaction.serialize().toString('base64'),
orderResponse,
}),
});
const result = await response.json();
return result.txSignature;
}
// Swap LSTs (convenience method)
async swapLst(params: {
inputMint: string;
outputMint: string;
amount: string;
signer: Keypair;
slippageBps?: number;
}): Promise<string> {
const quote = await this.getSwapQuote({
inputMint: params.inputMint,
outputMint: params.outputMint,
amount: params.amount,
mode: 'ExactIn',
signer: params.signer.publicKey.toBase58(),
slippageBps: params.slippageBps || 50,
});
return this.executeSwap(quote, params.signer);
}
}const connection = new Connection('https://api.mainnet-beta.solana.com');
const sanctum = new SanctumClient(API_KEY, connection);
// Get all LSTs
const lsts = await sanctum.getLsts();
console.log(`Found ${lsts.length} LSTs`);
// Get INF token info
const inf = await sanctum.getLst('INF');
console.log(`INF APY: ${inf.apy}%`);
// Swap 1 SOL to mSOL
const signature = await sanctum.swapLst({
inputMint: LST_MINTS.SOL,
outputMint: LST_MINTS.mSOL,
amount: '1000000000', // 1 SOL (9 decimals)
signer: wallet,
slippageBps: 50,
});
console.log('Swap TX:', signature);
// Swap mSOL to INF
const infSwap = await sanctum.swapLst({
inputMint: LST_MINTS.mSOL,
outputMint: LST_MINTS.INF,
amount: '500000000', // 0.5 mSOL
signer: wallet,
});// Deposit SOL or any LST to get INF
async function addLiquidity(
sanctum: SanctumClient,
lstMint: string,
amount: string,
signer: Keypair
): Promise<string> {
// Swap LST/SOL for INF
return sanctum.swapLst({
inputMint: lstMint,
outputMint: LST_MINTS.INF,
amount,
signer,
});
}
// Example: Add 10 SOL to Infinity
const tx = await addLiquidity(
sanctum,
LST_MINTS.SOL,
'10000000000',
wallet
);// Withdraw from Infinity pool
async function removeLiquidity(
sanctum: SanctumClient,
outputMint: string,
infAmount: string,
signer: Keypair
): Promise<string> {
// Swap INF for desired LST/SOL
return sanctum.swapLst({
inputMint: LST_MINTS.INF,
outputMint: outputMint,
amount: infAmount,
signer,
});
}
// Example: Withdraw to SOL
const tx = await removeLiquidity(
sanctum,
LST_MINTS.SOL,
'5000000000', // 5 INF
wallet
);| Operation | Fee |
|---|---|
| StakeWrappedSol | 0 bps |
| WithdrawWrappedSol | 1 bps |
| DepositStake | 10 bps (waived for SOL output) |
| SwapViaStake | Varies by route |
import { Jupiter } from '@jup-ag/core';
const jupiter = await Jupiter.load({
connection,
cluster: 'mainnet-beta',
user: wallet.publicKey,
});
// Jupiter automatically routes through Sanctum when optimal
const routes = await jupiter.computeRoutes({
inputMint: new PublicKey(LST_MINTS.mSOL),
outputMint: new PublicKey(LST_MINTS.jitoSOL),
amount: 1_000_000_000n,
slippageBps: 50,
});// Stake SOL to get LST
async function stakeSol(
sanctum: SanctumClient,
lstMint: string,
solAmount: string,
signer: Keypair
): Promise<string> {
return sanctum.swapLst({
inputMint: LST_MINTS.SOL,
outputMint: lstMint,
amount: solAmount,
signer,
});
}
// Stake 5 SOL for jupSOL
const tx = await stakeSol(
sanctum,
LST_MINTS.jupSOL,
'5000000000',
wallet
);// Instant unstake via Infinity/Reserve
async function instantUnstake(
sanctum: SanctumClient,
lstMint: string,
amount: string,
signer: Keypair
): Promise<string> {
return sanctum.swapLst({
inputMint: lstMint,
outputMint: LST_MINTS.SOL,
amount,
signer,
});
}// Withdraw to stake account (requires epoch wait)
async function delayedUnstake(
sanctum: SanctumClient,
lstMint: string,
amount: string,
signer: Keypair,
deactivate: boolean = true
): Promise<string> {
const url = new URL(`${sanctum.baseUrl}/swap/withdrawStake/order`);
url.searchParams.set('apiKey', sanctum.apiKey);
url.searchParams.set('lstMint', lstMint);
url.searchParams.set('amount', amount);
url.searchParams.set('signer', signer.publicKey.toBase58());
url.searchParams.set('deactivate', deactivate.toString());
const response = await fetch(url.toString());
const orderResponse = await response.json();
return sanctum.executeSwap(orderResponse, signer);
}// Get all LST APYs
async function getLstApys(sanctum: SanctumClient): Promise<Map<string, number>> {
const lsts = await sanctum.getLsts();
const apyMap = new Map();
for (const lst of lsts) {
apyMap.set(lst.symbol, lst.apy);
}
return apyMap;
}
// Get specific LST historical APY
async function getLstApyHistory(
sanctum: SanctumClient,
lstMint: string,
limit: number = 30
): Promise<Array<{epoch: number; apy: number}>> {
const response = await fetch(
`${sanctum.baseUrl}/lsts/${lstMint}/apys?apiKey=${sanctum.apiKey}&limit=${limit}`
);
const data = await response.json();
return data.apys;
}// Get TVL for specific LSTs
async function getTvl(
sanctum: SanctumClient,
symbols: string[]
): Promise<Map<string, number>> {
const tvlMap = new Map();
for (const symbol of symbols) {
const lst = await sanctum.getLst(symbol);
tvlMap.set(symbol, lst.tvl);
}
return tvlMap;
}import { getAccount, getAssociatedTokenAddress } from '@solana/spl-token';
async function getUserLstBalances(
connection: Connection,
wallet: PublicKey,
lstMints: string[]
): Promise<Map<string, bigint>> {
const balances = new Map();
for (const mint of lstMints) {
try {
const ata = await getAssociatedTokenAddress(
new PublicKey(mint),
wallet
);
const account = await getAccount(connection, ata);
balances.set(mint, account.amount);
} catch {
balances.set(mint, 0n);
}
}
return balances;
}| Program | ID | Description |
|---|---|---|
| S Controller | | Infinity pool management |
| Flat Fee Pricing | | Fee calculation (deprecated) |
| Flat Slab | | Current fee program |
| Calculator | ID |
|---|---|
| SPL | |
| SanctumSpl | |
| SanctumSplMulti | |
| Marinade | |
| Lido | |
| wSOL | |
| Program | ID |
|---|---|
| SanctumSpl Stake Pool | |
| SanctumSplMulti Stake Pool | |
sanctum/
├── SKILL.md # This file
├── resources/
│ ├── api-reference.md # Complete API documentation
│ ├── lst-reference.md # LST addresses and metadata
│ └── program-ids.md # All program addresses
├── examples/
│ ├── lst-swap/README.md # LST swap examples
│ ├── staking/README.md # Staking/unstaking examples
│ ├── infinity-pool/README.md # Infinity operations
│ └── liquidity/README.md # Add/remove liquidity
└── docs/
└── troubleshooting.md # Common issues