Migration Guide
Migrate from marginfi-client-v2 to p0-ts-sdk
Migration Guide: marginfi-client-v2 → p0-ts-sdk
Deprecation Notice: The @mrgnlabs/marginfi-client-v2 package is
officially deprecated as of March 15th, 2026. All integrators must migrate
to p0-ts-sdk to continue receiving updates, bug fixes, and new features.
This guide will help you migrate from marginfi-client-v2 to the new p0-ts-sdk. The new SDK has been completely rebuilt with a cleaner API, better TypeScript support, and improved developer experience.
Why Migrate?
- 🏗️ Modern Architecture: Built from scratch with TypeScript-first design
- 🔍 Transparent Transactions: You control when and how transactions are sent (no more hidden execution)
- 🎯 Dual API: Use classes (familiar) or standalone functions (flexible)
- 🔧 Unified Structure: Consistent patterns across all operations
- 🔀 Cross-venue Support: Native support for Kamino and Drift integrations
- 📊 Better DX: Improved error handling, better types, clearer documentation
Quick Comparison
| Feature | marginfi-client-v2 | p0-ts-sdk |
|---|---|---|
| Client Init | MarginfiClient.fetch() requires wallet | Project0Client.initialize() - wallet-free |
| Account Creation | Keypair-based (random addresses) | PDA-based (deterministic addresses) |
| Transactions | Auto-sends with processTransaction() | Returns transactions for you to sign/send |
| Account Loading | client.getMarginfiAccount() | client.fetchAccount() (convenience) |
| Account Discovery | Manual program query | client.getAccountAddresses(authority) |
| Bank Access | client.banks Map | client.banks array + helper methods |
| Deposits/Borrows | account.deposit() sends tx | account.makeDepositTx() returns tx |
| Vendor Exports | import { ... } from "marginfi-client-v2" | import { ... } from "@0dotxyz/p0-ts-sdk/vendor" |
Installation
# Remove old SDK
npm uninstall @mrgnlabs/marginfi-client-v2
# Install new SDK
npm install @0dotxyz/p0-ts-sdkBreaking Changes
1. Client Initialization
Old SDK (marginfi-client-v2):
import { MarginfiClient, getConfig } from "@mrgnlabs/marginfi-client-v2";
import { Connection } from "@solana/web3.js";
import { Wallet } from "@coral-xyz/anchor";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const config = getConfig("production");
// ❌ Wallet is REQUIRED
const client = await MarginfiClient.fetch(
config,
wallet, // Must provide wallet
connection
);New SDK (p0-ts-sdk):
import { Project0Client, getConfig } from "p0-ts-sdk";
import { Connection } from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const config = getConfig("production"); // Options changed, see below
// ✅ Wallet NOT required - read-only by default
const client = await Project0Client.initialize(connection, config);Config Options Changed:
- Old:
"production","staging","dev" - New:
"production","staging","staging-mainnet-clone","staging-alt"
Environment Descriptions:
production- Production program and mainnet configstaging- Staging program updated 2 weeks before each release, allowing integrators to account for changes in advancestaging-mainnet-clone- Same staging program asstaging, but with identical bank configuration as production (useful for testing against production-like data)staging-alt- Internal deployment for testing unreleased & early versions of program iterations (not recommended for integrators)
2. Account Creation
This is the biggest breaking change. The old SDK used random keypairs for accounts, while the new SDK uses PDAs (Program Derived Addresses) for deterministic account addresses.
Old SDK (marginfi-client-v2):
// ❌ Creates account with random keypair
const { marginfiAccount, signature } = await client.createMarginfiAccount();
// Account address is random: DfG8h...xyz (different every time)New SDK (p0-ts-sdk):
// ✅ Creates account with deterministic PDA
const createAccountTx = await client.createMarginfiAccountTx(
wallet.publicKey,
0 // account index
// 12345 // third party ID
);
// You sign and send the transaction
const signature = await wallet.sendTransaction(createAccountTx, connection);
await connection.confirmTransaction(signature);
// Account address is deterministic:
// Same wallet + same index = same account address every timeMigration Impact:
- 🔴 Existing accounts: Your old keypair-based accounts will continue to work! You can still load and use them.
- 🟢 New accounts: Will use the PDA system with deterministic addresses.
- 🟡 Account Index: You can now create multiple accounts per wallet using different indices.
Deriving Account Addresses:
import { deriveMarginfiAccount } from "p0-ts-sdk";
// Derive account address without RPC call
const [accountAddress] = deriveMarginfiAccount(
client.program.programId,
client.group.address,
wallet.publicKey,
0 // account index
// 12345 // third party ID
);
// This address is the same every time for:
// same wallet + same index + same program3. Loading Accounts
Old SDK (marginfi-client-v2):
// ❌ Method on client
const account = await client.getMarginfiAccount(accountAddress);
// Returns MarginfiAccountWrapper directly
await account.deposit(100, bankAddress);New SDK (p0-ts-sdk):
// ✅ Convenience method (recommended)
const wrappedAccount = await client.fetchAccount(accountAddress);
// Or manual approach
import { MarginfiAccount, MarginfiAccountWrapper } from "p0-ts-sdk";
const account = await MarginfiAccount.fetch(accountAddress, client.program);
const wrappedAccount = new MarginfiAccountWrapper(account, client);
// Now you can use it (but transactions work differently)
const depositTx = await wrappedAccount.makeDepositTx(100, bankAddress);Account Discovery:
The new SDK makes it easy to 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]);
}4. Transaction Handling (CRITICAL)
The new SDK never sends transactions automatically. You always get a transaction back that you must sign and send yourself.
Old SDK (marginfi-client-v2):
// ❌ Automatically signs and sends transaction
const signature = await account.deposit(
100,
bankAddress,
{}, // deposit options
{}, // process options
{} // tx options
);
console.log("Already sent:", signature);New SDK (p0-ts-sdk):
// ✅ Returns transaction for you to send
const depositTx = await wrappedAccount.makeDepositTx("100", bankAddress);
// YOU must sign and send
depositTx.feePayer = wallet.publicKey;
const signature = await wallet.sendTransaction(depositTx, connection);
await connection.confirmTransaction(signature);Why This Change?
- ✅ More Control: You decide when and how to send transactions
- ✅ Batch Operations: Easier to compose multiple operations
- ✅ Custom Signing: Support for hardware wallets, multisig, etc.
- ✅ Simulation: You can simulate before sending
- ✅ No Hidden Behavior: Transparent and predictable
5. Bank Access
Old SDK (marginfi-client-v2):
// Access banks Map directly
const bank = client.banks.get(bankAddress.toBase58());
// Helper methods
const bank = client.getBankByMint(USDC_MINT);
const bank = client.getBankByTokenSymbol("USDC");
const bank = client.getBankByPk(bankAddress);New SDK (p0-ts-sdk):
// Banks available as array or via helpers
const bank = client.getBank(bankAddress); // by address
const banks = client.getBanksByMint(USDC_MINT); // returns array
const allBanks = client.banks; // array of all banks
// AssetTag filtering (new feature)
import { AssetTag } from "p0-ts-sdk";
const usdcBanks = client.getBanksByMint(USDC_MINT, AssetTag.DEFAULT);
const solBanks = client.getBanksByMint(SOL_MINT, AssetTag.SOL);
const kaminoBanks = client.banks.filter(
(b) => b.config.assetTag === AssetTag.KAMINO
);6. Deposits
Old SDK (marginfi-client-v2):
// Sends transaction automatically
const signature = await account.deposit(
100, // amount
bankAddress,
{}, // depositOpts
{}, // processOpts
{} // txOpts
);New SDK (p0-ts-sdk):
// Returns transaction
const depositTx = await wrappedAccount.makeDepositTx(
"100", // amount as string
bankAddress
);
// Simulate (optional)
depositTx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
depositTx.feePayer = wallet.publicKey;
const simulation = await connection.simulateTransaction(depositTx);
// Send
const signature = await wallet.sendTransaction(depositTx, connection);
await connection.confirmTransaction(signature);7. Withdrawals
Old SDK (marginfi-client-v2):
const signature = await account.withdraw(
50,
bankAddress,
false // withdrawAll
);New SDK (p0-ts-sdk):
const withdrawTx = await wrappedAccount.makeWithdrawTx(
"50", // amount as string
bankAddress,
false // withdrawAll
);
const signature = await wallet.sendTransaction(withdrawTx, connection);
await connection.confirmTransaction(signature);8. Borrows
Old SDK (marginfi-client-v2):
const signature = await account.borrow(100, bankAddress);New SDK (p0-ts-sdk):
// Check max borrow first
const maxBorrow = wrappedAccount.computeMaxBorrowForBank(bankAddress);
console.log(`Max borrow: ${maxBorrow.toString()}`);
// Build borrow transaction(s)
const borrowResult = await wrappedAccount.makeBorrowTx("100", bankAddress);
// ⚠️ May return MULTIPLE transactions
console.log(`Transactions: ${borrowResult.transactions.length}`);
// Send all transactions in order
for (const tx of borrowResult.transactions) {
tx.feePayer = wallet.publicKey;
const sig = await wallet.sendTransaction(tx, connection);
await connection.confirmTransaction(sig);
}Important: Some operations (borrow, repay with collateral, loops) may return multiple transactions that must be executed in order.
9. Repayment
Old SDK (marginfi-client-v2):
// Repay all
const signature = await account.repay(0, bankAddress, true);
// Repay specific amount
const signature = await account.repay(50, bankAddress, false);New SDK (p0-ts-sdk):
// Repay specific amount
const repayTx = await wrappedAccount.makeRepayTx(
"50",
bankAddress,
false // repayAll
);
// Repay all
const repayAllTx = await wrappedAccount.makeRepayTx(
"0", // amount ignored when repayAll = true
bankAddress,
true // repayAll
);
const signature = await wallet.sendTransaction(repayTx, connection);
await connection.confirmTransaction(signature);10. Vendor Exports
Heavy dependencies (oracles, protocol integrations) are now in a separate entry point to reduce bundle size.
Old SDK (marginfi-client-v2):
import {
fetchPythPrice,
fetchSwitchboardPrice,
// ... all vendor exports mixed in
} from "@mrgnlabs/marginfi-client-v2";New SDK (p0-ts-sdk):
// Main exports (lightweight)
import { Project0Client, MarginfiAccount } from "p0-ts-sdk";
// Vendor exports (separate)
import {
fetchOracleData,
// ... oracle utilities
} from "@0dotxyz/p0-ts-sdk/vendor";Common Patterns
Pattern: Deposit Flow
// Complete deposit example
import { Project0Client } from "p0-ts-sdk";
// 1. Initialize client (once)
const client = await Project0Client.initialize(connection, config);
// 2. Load account
const wrappedAccount = await client.fetchAccount(accountAddress);
// 3. Get bank
const usdcBanks = client.getBanksByMint(USDC_MINT);
const usdcBank = usdcBanks[0];
// 4. Build deposit transaction
const depositTx = await wrappedAccount.makeDepositTx("100", usdcBank.address);
// 5. Sign and send
depositTx.feePayer = wallet.publicKey;
const signature = await wallet.sendTransaction(depositTx, connection);
await connection.confirmTransaction(signature);Pattern: Borrow Flow
// Complete borrow example
const usdcBank = client.getBanksByMint(USDC_MINT)[0];
// 1. Check borrow capacity
const maxBorrow = wrappedAccount.computeMaxBorrowForBank(usdcBank.address);
console.log(`Max borrow: ${maxBorrow.toString()} USDC`);
// 2. Build borrow transaction(s)
const borrowResult = await wrappedAccount.makeBorrowTx("50", usdcBank.address);
// 3. Handle multi-transaction case
console.log(`Transactions to send: ${borrowResult.transactions.length}`);
// 4. Send all transactions in order
for (const tx of borrowResult.transactions) {
tx.feePayer = wallet.publicKey;
const sig = await wallet.sendTransaction(tx, connection);
await connection.confirmTransaction(sig);
}Pattern: Create and Fund New Account
// 1. Create account transaction
const createAccountTx = await client.createMarginfiAccountTx(
wallet.publicKey,
0
);
// 2. Send creation transaction
const createSig = await wallet.sendTransaction(createAccountTx, connection);
await connection.confirmTransaction(createSig);
// 3. Derive the account address
import { deriveMarginfiAccount } from "p0-ts-sdk";
const [accountAddress] = deriveMarginfiAccount(
client.program.programId,
client.group.address,
wallet.publicKey,
0
);
// 4. Load the newly created account
const wrappedAccount = await client.fetchAccount(accountAddress);
// 5. Deposit into it
const depositTx = await wrappedAccount.makeDepositTx("100", bankAddress);
const depositSig = await wallet.sendTransaction(depositTx, connection);
await connection.confirmTransaction(depositSig);Getting Help
- Examples: View runnable examples
- GitHub Issues: Report issues or ask questions
- Discord: Join our developer community
Summary Checklist
Use this checklist to track your migration progress:
- Installed
@0dotxyz/p0-ts-sdkand removed@mrgnlabs/marginfi-client-v2 - Updated client initialization (removed wallet requirement)
- Updated account loading (use
client.fetchAccount()convenience method) - Updated account discovery (use
client.getAccountAddresses()if needed) - Changed all
account.deposit()→account.makeDepositTx() - Changed all
account.withdraw()→account.makeWithdrawTx() - Changed all
account.borrow()→account.makeBorrowTx() - Changed all
account.repay()→account.makeRepayTx() - Added manual transaction signing and sending for all operations
- Handle multi-transaction operations (borrow, repay with collateral)
- Updated bank access patterns (arrays + AssetTag)
- Moved vendor imports to
@0dotxyz/p0-ts-sdk/vendor - Updated account creation to use PDAs
- Tested all critical user flows
- Updated error handling for transaction confirmation
- Documented account indices for future reference
Timeline
- Now - March 15th, 2026: Migration period
- March 15th, 2026:
marginfi-client-v2officially deprecated (no more updates) - After March 15th: Only
@0dotxyz/p0-ts-sdkwill receive updates and support
Start your migration today to ensure a smooth transition!