Getting Started
Get started with the P0 TypeScript SDK
The p0-ts-sdk is the official SDK for interacting with the marginfi protocol on Solana. This SDK has been completely rebuilt from the ground up with a modern architecture, uniform structure, and support for multiple venues (Project 0, Kamino, and soon Drift and Jupiter Lend).
Migration Notice: We strongly advise all current integrators of the
@mrgnlabs/marginfi-client-v2 package to migrate to the p0-ts-sdk. The
marginfi-client-v2 will officially be no longer maintained as of March 15th,
2026. See our migration guide for detailed
instructions.
Installation
Install the SDK using your preferred package manager:
npm install @0dotxyz/p0-ts-sdk
# or
yarn add @0dotxyz/p0-ts-sdk
# or
pnpm add @0dotxyz/p0-ts-sdkSolana Dependencies
The SDK uses the following Solana dependencies versions:
@solana/web3.js: 1.98.2@coral-xyz/anchor: ^0.30.1
Quick Start
This quick start guide demonstrates the class-based API, which will be familiar to users of the legacy marginfi-client-v2 SDK.
If you prefer a more functional approach or need custom integrations using only the underlying functions, see our Advanced Usage guide.
Step 1: Initialize the Client
The Project0Client is the main entry point for interacting with the protocol. It loads all banks, oracle prices, and protocol configuration at initialization.
import { Connection } from "@solana/web3.js";
import { Project0Client, getConfig } from "p0-ts-sdk";
// Connect to Solana
const connection = new Connection(
"https://api.mainnet-beta.solana.com",
"confirmed"
);
// Get configuration for your environment
// Options: "production", "staging", "staging-mainnet-clone", "staging-alt"
const config = getConfig("production");
// Initialize the client - loads all banks and oracle prices
const client = await Project0Client.initialize(connection, config);
console.log(`✅ Loaded ${client.banks.length} banks`);The client initialization fetches all protocol data upfront. Reuse the client instance throughout your application to avoid redundant RPC calls.
Step 2: Create Your Marginfi Account
If you don't have a marginfi account yet, you'll need to create one:
// Create account transaction
const createAccountTx = await client.createMarginfiAccountTx(
wallet.publicKey,
0 // account index (default: 0)
);
// Sign and send the transaction
const signature = await wallet.sendTransaction(createAccountTx, connection);
await connection.confirmTransaction(signature);
console.log(`✅ Account created!`);Parameters:
accountIndex- Allows a single wallet to create multiple marginfi accounts. Each index creates a unique PDA (Program Derived Address) for that wallet. This is a u16 value.thirdPartyId(optional) - An optional identifier for third-party integrations to create isolated account spaces. This is also a u16 value.
// Create a second account for the same wallet
const secondAccountTx = await client.createMarginfiAccountTx(
wallet.publicKey,
1 // account index 1
);
// Create an account with a third-party identifier
const thirdPartyAccountTx = await client.createMarginfiAccountTx(
wallet.publicKey,
0,
12345 // third party ID
);Advanced: If you need to compose account creation with other operations, use the projection method:
// Get a projected account wrapper before the account exists on-chain
const { wrappedAccount, ix } = await client.createMarginfiAccountWithProjection(
wallet.publicKey,
0
);
// The wrappedAccount can be used to build transactions that will execute
// after the account is created (by including the ix in your transaction)Step 3: Load Your Marginfi Account
Option 1: Direct fetch (convenience method)
import { PublicKey } from "@solana/web3.js";
const accountAddress = new PublicKey("YOUR_MARGINFI_ACCOUNT_ADDRESS");
// Fetch and wrap in one call
const wrappedAccount = await client.fetchAccount(accountAddress);Option 2: Manual fetch and wrap
import { MarginfiAccount, MarginfiAccountWrapper } from "p0-ts-sdk";
const accountAddress = new PublicKey("YOUR_MARGINFI_ACCOUNT_ADDRESS");
// Fetch the raw account data
const account = await MarginfiAccount.fetch(accountAddress, client.program);
// Wrap it for a cleaner API
const wrappedAccount = new MarginfiAccountWrapper(account, client);Discovering Accounts:
If you don't know the account address, you can discover all accounts for a wallet:
// Find all accounts owned by a wallet
const accountAddresses = await client.getAccountAddresses(wallet.publicKey);
console.log(`Found ${accountAddresses.length} accounts`);
// Load the first account
if (accountAddresses.length > 0) {
const wrappedAccount = await client.fetchAccount(accountAddresses[0]);
}Understanding PDAs: Marginfi accounts are PDAs (Program Derived Addresses) deterministically derived from your wallet address, account index, and optional third-party ID. You can derive the account address without fetching from chain:
import { deriveMarginfiAccount } from "p0-ts-sdk";
// Derive the account address for your wallet at index 0
const [accountAddress] = deriveMarginfiAccount(
client.program.programId,
client.group.address,
wallet.publicKey,
0, // account index
// 12345 // third party ID
);
// Now fetch and load the account
const account = await MarginfiAccount.fetch(accountAddress, client.program);
const wrappedAccount = new MarginfiAccountWrapper(account, client);This is useful when you know the account index but don't have the address stored. The PDA derivation ensures the same inputs always produce the same account address.
The MarginfiAccountWrapper provides convenient methods for all account operations while automatically accessing the client's banks, oracles, and other cached data.
Step 4: Find a Bank
Banks represent lending pools for specific tokens. You can find banks by mint address or by bank address:
import { AssetTag } from "p0-ts-sdk";
// Get all USDC banks
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
const usdcBanks = client.getBanksByMint(USDC_MINT);
// Or get a specific bank by address
const bank = client.getBank(new PublicKey("BANK_ADDRESS"));Understanding Asset Tags
The SDK uses AssetTag to determine what kinds of assets can be held together in a single account. This prevents incompatible positions from being comingled:
AssetTag.DEFAULT(0) - Regular assets (USDC, USDT, etc.) that can be comingled with other DEFAULT assets or with SOLAssetTag.SOL(1) - Only assigned to the native SOL bank. Accounts with SOL can hold either DEFAULT assets or STAKED assets, but not bothAssetTag.STAKED(2) - Native staked SOL assets (LSTs). Accounts with STAKED positions can only deposit other STAKED assets or SOL, and can only borrow SOLAssetTag.KAMINO(3) - Kamino integration banksAssetTag.DRIFT(4) - Drift integration banks
Comingling Rules:
- ✅ DEFAULT assets can mix with other DEFAULT assets and SOL
- ✅ SOL can mix with DEFAULT assets OR STAKED assets (but not both in the same account)
- ✅ STAKED assets can only mix with other STAKED assets or SOL
- ❌ You cannot have both DEFAULT and STAKED assets in the same account (unless only SOL is present)
// Most tokens like USDC use AssetTag.DEFAULT
const usdcBank = client.getBanksByMint(USDC_MINT, AssetTag.DEFAULT)[0];
// SOL bank is always AssetTag.SOL
const solBank = client.getBanksByMint(SOL_MINT, AssetTag.SOL)[0];
// Staked SOL assets (LSTs) use AssetTag.STAKED
const lstBanks = client.banks.filter(
(b) => b.config.assetTag === AssetTag.STAKED
);
// Kamino integration banks
const kaminoBanks = client.banks.filter(
(b) => b.config.assetTag === AssetTag.KAMINO
);
// Drift integration banks
const driftBanks = client.banks.filter(
(b) => b.config.assetTag === AssetTag.DRIFT
);Step 5: Deposit Tokens
Deposit tokens into a bank to earn interest and build collateral:
// Select a bank (using USDC from previous example)
const usdcBank = usdcBanks[0];
// Build the deposit transaction (amount in UI units)
const depositTx = await wrappedAccount.makeDepositTx(
usdcBank.address,
"100" // Deposit 100 USDC
);
// Simulate the transaction (optional but recommended)
const recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
depositTx.recentBlockhash = recentBlockhash;
depositTx.feePayer = wallet.publicKey;
const simulation = await connection.simulateTransaction(depositTx);
console.log(`Compute units: ${simulation.value.unitsConsumed}`);
// Sign and send the transaction
// depositTx.sign([wallet]);
// const signature = await connection.sendTransaction(depositTx);
// await connection.confirmTransaction(signature);Step 6: Borrow Against Collateral
Once you have collateral deposited, you can borrow against it:
// Check your maximum borrow capacity
const usdcBank = client.getBanksByMint(USDC_MINT, AssetTag.DEFAULT)[0];
const maxBorrow = wrappedAccount.computeMaxBorrowForBank(usdcBank.address);
console.log(`Max borrow: ${maxBorrow.toString()} USDC`);
// Build the borrow transaction
const borrowResult = await wrappedAccount.makeBorrowTx(
usdcBank.address,
"100" // Borrow 100 USDC
);
// Handle multi-transaction bundles
console.log(`Transactions in bundle: ${borrowResult.transactions.length}`);
console.log(`Main action at index: ${borrowResult.actionTxIndex}`);
// If your RPC supports bundles, you can simulate multiple transactions at once
import { simulateBundle } from "p0-ts-sdk";
const results = await simulateBundle(
connection.rpcEndpoint,
borrowResult.transactions
);
// Sign and send all transactions in order
// for (const tx of borrowResult.transactions) {
// tx.sign([wallet]);
// const sig = await connection.sendTransaction(tx);
// await connection.confirmTransaction(sig);
// }Some operations (like borrowing) may return multiple transactions that need to
be executed in order. Always check borrowResult.transactions.length and send
all transactions.
Common Use Cases
Repaying Borrowed Amounts
// Repay some or all of your borrowed amount
const repayTx = await wrappedAccount.makeRepayTx(
usdcBank.address,
"50", // Repay 50 USDC
true // repayAll = false (set to true to repay everything)
);
// Send transaction...Withdrawing Deposits
// Withdraw your deposits (respecting health requirements)
const withdrawTx = await wrappedAccount.makeWithdrawTx(
solBank.address,
"0.5", // Withdraw 0.5 SOL
false // withdrawAll = false
);
// Send transaction...Running the Examples
The SDK includes 7+ runnable examples in the examples/ directory:
# Clone the SDK repository
git clone https://github.com/0dotxyz/p0-ts-sdk.git
cd p0-ts-sdk/examples
# Configure your environment
cp .env.example .env
# Edit .env with your values
# Run any example
npx tsx 01-deposit.ts
npx tsx 02-borrow.tsEach example runs in simulation mode by default, so you can test without spending gas or risking funds.
Next Steps
Now that you've completed the quick start, explore these topics:
- Advanced Usage - Use standalone functions for custom integrations
- Migration Guide - Migrate from marginfi-client-v2
- API Reference - Complete API documentation (Coming Soon)
- Error Handling - Best practices for handling errors (Coming Soon)
Getting Help
- GitHub Issues: Report bugs or request features
- Discord: Join our developer community
TypeScript Support
The SDK is written in TypeScript and exports all types:
import type {
MarginfiAccountType,
BankType,
BalanceType,
OraclePrice,
Project0Config,
MarginRequirementType,
} from "p0-ts-sdk";Enable strict mode in your tsconfig.json for the best development experience:
{
"compilerOptions": {
"strict": true,
"moduleResolution": "bundler",
"target": "ES2022"
}
}