This directory contains practical examples demonstrating how to use the Project 0 SDK.
The examples are organized by functionality, from basic operations to advanced use cases. Each example is a standalone TypeScript file with detailed comments.
# Install dependencies (from monorepo root)
pnpm install
# Or install dotenv if running standalone
pnpm add dotenvStep 1: Copy the example environment file:
cd examples
cp .env.example .envStep 2: Edit .env and fill in your values:
# Required
MARGINFI_GROUP_ADDRESS=4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8
MARGINFI_PROGRAM_ID=MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA
MARGINFI_ACCOUNT_ADDRESS=<your_account_address>
# Optional - defaults are provided
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
MARGINFI_ENVIRONMENT=productionNote: The .env file is gitignored for security. Never commit private keys!
Deposit tokens into a bank to earn interest and use as collateral.
# From the examples directory
ts-node 01-deposit.ts
# Or with tsx (recommended)
pnpm exec tsx 01-deposit.tsWhat you'll learn:
- Initialize the Project0Client
- Fetch a marginfi account
- Create a wrapper for clean API
- Deposit tokens into a bank
Borrow tokens against your collateral.
ts-node examples/02-borrow.tsWhat you'll learn:
- Calculate maximum borrow capacity
- Create borrow instructions
- Check health factor before borrowing
Withdraw your deposited collateral.
ts-node examples/03-withdraw.tsWhat you'll learn:
- Calculate maximum withdraw amount
- Withdraw partial or full positions
- Maintain account health
Repay borrowed tokens to reduce liabilities.
ts-node examples/04-repay.tsWhat you'll learn:
- Check current liabilities
- Repay partial or full debt
- Improve account health
Fetch and update oracle prices for all banks.
ts-node examples/05-oracle-prices.tsWhat you'll learn:
- Access real-time oracle prices
- Manually refresh price data
- Work with Pyth and Switchboard oracles
- Understand price confidence intervals
Monitor your account's health and risk metrics.
ts-node examples/06-account-health.tsWhat you'll learn:
- Compute health components (assets vs liabilities)
- Calculate health factor
- Monitor free collateral
- Track net APY
- Access health cache
Calculate borrowing capacity and available collateral.
ts-node examples/07-remaining-collateral.tsWhat you'll learn:
- Calculate free collateral in USD
- Determine max borrow per bank
- Check max withdraw per position
- Monitor account utilization
Filter banks by mint address and asset tag.
ts-node 10-bank-filtering.tsWhat you'll learn:
- Use
getBanksByMint()to get all banks matching a mint - Filter by AssetTag (DEFAULT, KAMINO, STAKED)
- Distinguish between main protocol and Kamino banks
- Handle cases where multiple banks exist for the same mint
- Select specific banks from the returned array
Repay debt by swapping collateral assets.
ts-node examples/08-repay-with-collateral.tsWhat you'll learn:
- Withdraw collateral
- Swap via Jupiter
- Repay debt in one transaction
- Handle complex multi-step operations
Create leveraged positions by looping deposits and borrows.
ts-node examples/09-loop-leverage.tsWhat you'll learn:
- Build leveraged positions
- Use Jupiter swaps for leverage
- Deposit → Borrow → Swap → Deposit loops
- Maximize capital efficiency
Convert a native stake account into LST tokens via the single-validator pool.
pnpm exec tsx 12-mint-staked-lst.tsWhat you'll learn:
- Convert native stake to LST tokens
- Handle partial vs full stake account conversion
- Authorize pool as staker/withdrawer
- Deposit stake into a single-validator pool
Convert LST tokens back into a native stake account.
pnpm exec tsx 13-redeem-staked-lst.tsWhat you'll learn:
- Redeem LST tokens to a new stake account
- Approve mint authority to burn LST
- Withdraw stake from the pool
Merge two native stake accounts into one.
pnpm exec tsx 14-merge-stake-accounts.tsWhat you'll learn:
- Merge a source stake account into a destination
- Requirements: same authority, same validator, both active
The central client that manages all marginfi interactions:
const client = await Project0Client.initialize(connection, {
environment: "production",
groupPk: new PublicKey("YOUR_GROUP_ADDRESS"),
programId: new PublicKey("YOUR_PROGRAM_ID"),
});
// Access preloaded data
client.bankMap // Map of all banks
client.oraclePriceByBank // Current oracle prices
client.mintDataByBank // Token program data (keyed by bank address)
client.addressLookupTables // For transaction optimization
// Get banks
client.getBank(address) // Get bank by address
client.getBanksByMint(mint, tag?) // Get all banks by mint (+ optional tag filter)Clean API wrapper around MarginfiAccount:
// Create wrapper
const wrappedAccount = new MarginfiAccountWrapper(account, client);
// Clean method calls - no need to pass banks, oracles, etc.
await wrappedAccount.makeDepositIx(bankAddress, amount);
const health = wrappedAccount.computeFreeCollateral();
const maxBorrow = wrappedAccount.computeMaxBorrowForBank(bankAddress);Before any operation, check your account health:
const freeCollateral = wrappedAccount.computeFreeCollateral();
const healthComponents = wrappedAccount.computeHealthComponents(
MarginRequirementType.Maintenance
);Never hardcode amounts - always check limits:
const maxBorrow = wrappedAccount.computeMaxBorrowForBank(bankAddress);
const maxWithdraw = wrappedAccount.computeMaxWithdrawForBank(bankAddress);try {
const ix = await wrappedAccount.makeBorrowIx(bankAddress, amount);
// Process instruction
} catch (error) {
console.error("Failed to create borrow instruction:", error);
// Handle error appropriately
}For critical operations, refresh prices first:
const { bankOraclePriceMap } = await fetchOracleData(client.banks, {
pythOpts: { mode: "on-chain", connection },
swbOpts: { mode: "on-chain", connection },
});// Initialize at app startup
const client = await Project0Client.initialize(connection, config);
// Reuse throughout your app
function depositHandler() {
const account = await MarginfiAccount.fetch(userAddress, client.program);
const wrapped = new MarginfiAccountWrapper(account, client);
// ... perform operations
}// Check multiple banks at once
const activePairs = wrappedAccount.computeActiveEmodePairs(emodePairs);
const impacts = wrappedAccount.computeEmodeImpacts(emodePairs, bankAddresses);import BigNumber from "bignumber.js";
// Always use BigNumber for precision
const amount = new BigNumber(userInput);
const maxAmount = wrappedAccount.computeMaxBorrowForBank(bank.address);
if (amount.gt(maxAmount)) {
throw new Error(`Amount exceeds maximum: ${maxAmount.toString()}`);
}All examples include full type safety:
import {
Project0Client,
MarginfiAccount,
MarginfiAccountWrapper,
MarginRequirementType,
Bank,
OraclePrice,
} from "p0-ts-sdk";Ensure you're using the correct mint address and the bank exists in the client's bankMap:
const banks = client.getBanksByMint(mintAddress);
if (banks.length === 0) {
console.log("Available banks:", Array.from(client.bankMap.keys()));
}
const bank = banks[0]; // Use first matching bankCheck your account health before borrowing:
const freeCollateral = wrappedAccount.computeFreeCollateral();
console.log("Free collateral:", freeCollateral.toString());For complex transactions (loop, repay with collat), you may need to use versioned transactions with lookup tables:
// Lookup tables are automatically loaded in client.addressLookupTables
const tx = new VersionedTransaction(message);Found an issue or want to add an example? Contributions are welcome!
- Add your example to this directory
- Follow the existing naming convention (
##-feature-name.ts) - Include detailed comments and error handling
- Update this README with your example
These examples are for educational purposes. Always:
- Test on devnet first
- Use small amounts initially
- Understand the risks of leveraged positions
- Monitor your account health regularly
- Never share your private keys