TypeScript SDK
What this page is for: the canonical reference for @rflowdapp/rflow — how to install it, configure a client, and invoke every flow with full TypeScript types.
Installation#
The SDK has three peer dependencies. Install everything together:
Peer dependency versions
@coral-xyz/anchor ^0.32.0 · @solana/web3.js ^1.95.0 · @solana/spl-token ^0.4.0Quick start#
Read-only client
The fastest way to start: a read-only client. No wallet, no signing — just on-chain reads.
1import { RFlowClient, formatAmount } from "@rflowdapp/rflow";2import { Connection } from "@solana/web3.js";34const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");5const client = RFlowClient.readOnly(connection);67const config = await client.getConfig();8console.log("Protocol fee:", (config?.feeBps ?? 0) / 100, "%");910const deals = await client.yieldDeals.getAvailableDeals();11deals.forEach((d) => {12 console.log(`#${d.dealId} · ${formatAmount(d.sellingPrice, 6)} USDC`);13});Client with a wallet
For write operations, pass a wallet that satisfies Anchor's Wallet interface (publicKey, signTransaction, signAllTransactions):
1import { RFlowClient } from "@rflowdapp/rflow";2import { Connection } from "@solana/web3.js";3import { Wallet } from "@coral-xyz/anchor";45// Option A — server / script with a Keypair6import { Keypair } from "@solana/web3.js";7const wallet = new Wallet(Keypair.fromSecretKey(/* your key */ new Uint8Array()));89// Option B — browser with wallet-adapter10// const { publicKey, signTransaction, signAllTransactions } = useWallet();11// const wallet = { publicKey, signTransaction, signAllTransactions };1213const client = new RFlowClient({14 connection: new Connection("https://api.mainnet-beta.solana.com", "confirmed"),15 wallet,16});Yield Deals#
The client.yieldDeals sub-client covers every flow for receipt-token deals (mSOL, jitoSOL, bSOL, kUSDC, cUSDC).
Reads
1// By deal id2const deal = await client.yieldDeals.getDeal(42);34// By PDA5const dealByPda = await client.yieldDeals.getDealByPda(somePda);67// All deals, optionally filtered8import { DealStatus, SourceProtocol } from "@rflowdapp/rflow";910const all = await client.yieldDeals.getAllDeals({11 status: DealStatus.Active,12 sourceProtocol: SourceProtocol.Marinade,13});1415// Convenience queries16const open = await client.yieldDeals.getAvailableDeals();17const mySells = await client.yieldDeals.getDealsBySeller(myPubkey);18const myBuys = await client.yieldDeals.getDealsByBuyer(myPubkey);Create a yield deal
1import {2 SourceProtocol,3 KNOWN_MINTS,4 VALID_DURATIONS,5} from "@rflowdapp/rflow";6import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";78// Sell 10 mSOL of future appreciation for 90 days9const ixs = await client.yieldDeals.createDeal({10 receiptTokenMint: KNOWN_MINTS.MSOL,11 receiptTokensAmount: 10_000_000_000, // 10 mSOL (9 decimals)12 principalValueAtLock: 1_180_000_000, // ~$1,180 (6 decimals)13 expectedYield: 17_000_000, // ~$17 expected14 sellingPrice: 14_000_000, // List for $1415 durationDays: 90, // see VALID_DURATIONS16 sourceProtocol: SourceProtocol.Marinade,17 exchangeRateAtLock: 1_180_000, // 1.18 scaled 1e618 // Mainnet only — Pyth price update PDA for the receipt mint19 // priceUpdate: msolUsdPriceUpdatePubkey,20});2122const tx = new Transaction().add(...ixs);23const sig = await sendAndConfirmTransaction(connection, tx, [keypair]);VALID_DURATIONS
30 | 60 | 90 | 180 | 365. The SDK throws InvalidDurationError for anything else.Buy a yield deal
1import { getAssociatedTokenAddress } from "@solana/spl-token";23const dealId = 42;4const deal = await client.yieldDeals.getDeal(dealId);5const config = await client.getConfig();67const sellerPaymentAccount = await getAssociatedTokenAddress(8 deal!.paymentMint,9 deal!.seller10);11const treasuryAccount = await getAssociatedTokenAddress(12 deal!.paymentMint,13 config!.treasury14);1516const ixs = await client.yieldDeals.buyDeal(17 dealId,18 sellerPaymentAccount,19 treasuryAccount20);2122const tx = new Transaction().add(...ixs);23await sendAndConfirmTransaction(connection, tx, [keypair]);Cancel · Settle · Buyback
1// Cancel — seller only, before fill2const cancelIxs = await client.yieldDeals.cancelDeal(dealId);34// Settle — permissionless after ends_at5// currentTokenValue is the current USD value of the locked tokens.6// Mainnet LSTs: pass a Pyth priceUpdate too; oracle takes precedence.7const settleIxs = await client.yieldDeals.settleDeal(8 dealId,9 currentTokenValue,10 /* priceUpdate? */ undefined11);1213// Buyback — seller-only early exit with penalty14const buybackIxs = await client.yieldDeals.buybackDeal(15 dealId,16 currentTokenValue,17 /* priceUpdate? */ undefined18);Meteora LP Deals#
The client.meteoraDeals sub-client mirrors the yield deal API for Meteora DAMM v2 positions. Sellers lock a Position NFT; buyers claim pool fees during the active deal.
Create a Meteora LP deal
1const ixs = await client.meteoraDeals.createDeal({2 // Position3 positionNftMint: positionNftMintPubkey,4 positionAccount: meteoraPositionPda,5 pool: meteoraPoolPda,6 tokenAMint: usdcMint,7 tokenBMint: solMint,8 // Fee snapshots at lock — read these from the position state9 feeAAtLock: 0n,10 feeBAtLock: 0n,11 // Projections — your call, used for UI / discount math12 expectedFeeA: 500_000_000n,13 expectedFeeB: 1_000_000_000n,14 expectedFeeValueUsdc: 650_000_000n,15 sellingPrice: 500_000_000n,16 durationDays: 90,17});Claim fees during the deal
Only the buyer can claim. The SDK builds the CPI into Meteora; you supply the pool-side accounts.
1import { PublicKey } from "@solana/web3.js";23const METEORA_PROGRAM = new PublicKey(4 "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"5);67const claimIxs = await client.meteoraDeals.claimFees({8 dealId,9 meteoraProgram: METEORA_PROGRAM,10 meteoraPosition: positionPda,11 meteoraPool: poolPda,12 poolTokenAVault: tokenAVault,13 poolTokenBVault: tokenBVault,14});Settle · Cancel · Split · Withdraw
1// Settle after expiry — permissionless. Returns the NFT to the seller.2const settleIxs = await client.meteoraDeals.settleDeal(dealId);34// Cancel before fill — seller only5const cancelIxs = await client.meteoraDeals.cancelDeal(dealId);67// Split a position before listing (so you sell only a portion of it)8const splitIxs = await client.meteoraDeals.splitPosition({9 sourceNftMint: sourceNftMint,10 sourcePosition: sourcePositionPda,11 targetNftMint: newPositionNftMint,12 targetPosition: newPositionPda,13 meteoraPool: poolPda,14 meteoraProgram: METEORA_PROGRAM,15 eventAuthority: meteoraEventAuthority,16 unlockedLiquidityPercentage: 50,17 permanentLockedLiquidityPercentage: 50,18 feeAPercentage: 50,19 feeBPercentage: 50,20 reward0Percentage: 50,21 reward1Percentage: 50,22});2324// Withdraw liquidity after settlement — seller only25const withdrawIxs = await client.meteoraDeals.withdrawLiquidity({26 dealId,27 meteoraProgram: METEORA_PROGRAM,28 meteoraPosition: positionPda,29 meteoraPool: poolPda,30 poolTokenAVault: tokenAVault,31 poolTokenBVault: tokenBVault,32 poolAuthority: poolAuthorityPda,33 eventAuthority: meteoraEventAuthority,34 tokenAProgram: TOKEN_PROGRAM_ID,35 tokenBProgram: TOKEN_PROGRAM_ID,36 tokenAAmountThreshold: 0n,37 tokenBAmountThreshold: 0n,38});PDA Helpers#
Every PDA the program uses is exposed as a pure helper. They work without a connection or wallet.
1import {2 findProtocolConfigPDA,3 findYieldDealPDA,4 findVaultPDA,5 findMeteoraLpDealPDA,6 findMeteoraVaultPDA,7 PROGRAM_ID,8} from "@rflowdapp/rflow";910const [configPda] = findProtocolConfigPDA(PROGRAM_ID);11const [dealPda] = findYieldDealPDA(dealId, PROGRAM_ID);12const [vaultPda] = findVaultPDA(dealPda, PROGRAM_ID);1314const [meteoraDealPda] = findMeteoraLpDealPDA(dealId, PROGRAM_ID);15const [nftVaultPda] = findMeteoraVaultPDA(meteoraDealPda, PROGRAM_ID);1617// Or via the client (no need to pass PROGRAM_ID)18const [pda] = client.yieldDeals.findDealPda(dealId);19const [vault] = client.yieldDeals.findVaultPda(pda);Error handling#
The SDK throws typed errors for input mistakes and provides helpers for parsing Anchor errors that come back from the chain.
1import {2 RFlowError,3 FetchError,4 DealNotFoundError,5 InvalidInputError,6 InvalidDurationError,7 ProtocolPausedError,8 parseAnchorError,9 isAccountNotFoundError,10} from "@rflowdapp/rflow";1112// Input validation throws synchronously13try {14 await client.yieldDeals.createDeal({ /* bad input */ } as any);15} catch (err) {16 if (err instanceof InvalidDurationError) {17 console.error("durationDays must be one of", err.validDurations);18 } else if (err instanceof InvalidInputError) {19 console.error("Bad input:", err.message);20 }21}2223// Read errors24try {25 const deal = await client.yieldDeals.getDeal(999);26} catch (err) {27 if (err instanceof DealNotFoundError) {28 console.warn("No deal at id", err.dealId);29 } else if (isAccountNotFoundError(err)) {30 console.warn("Account does not exist on chain");31 }32}3334// On-chain errors come back wrapped — parse them35try {36 await sendAndConfirmTransaction(connection, tx, [keypair]);37} catch (err) {38 const parsed = parseAnchorError(err);39 if (parsed) {40 console.error("Program error", parsed.code, parsed.message);41 }42}Types#
All public types are re-exported from the package root. Everything is strictly typed — there are no any escape hatches in the public surface.
1import type {2 // Deal types3 YieldDeal,4 MeteoraLpDeal,5 ProtocolConfig,6 // Input types7 CreateYieldDealInput,8 CreateMeteoraLpDealInput,9 ClaimMeteoraFeesInput,10 WithdrawMeteoraLiquidityInput,11 SplitMeteoraPositionInput,12 DealFilters,13 // Enums14 DealStatus,15 SourceProtocol,16 DealDuration,17} from "@rflowdapp/rflow";1819// Status & protocol enums match the program IDL20enum DealStatus { Created, Active, Settled, Cancelled, BoughtBack }21enum SourceProtocol {22 Kamino, Solend, Save,23 Marinade, Jito, Blaze, Sanctum,24 RaydiumLp, MeteoraLp, OrcaLp,25 FeeStream,26}2728// Durations are a union — the compiler rejects anything else29type DealDuration = 30 | 60 | 90 | 180 | 365;Tips for production
connection. Batch your write transactions with createSignedVersionedTransaction from web3.js if you need priority fees, and keep one RFlowClient instance per wallet — it caches the Anchor Program.