Quick Start
What this page is for: get a TypeScript app talking to rFlow on mainnet — read the protocol config, list a yield deal, and fill one — in under five minutes.
What you’ll need
Prerequisites#
- Node.js 18+ (or Bun 1.0+)
- A Solana wallet — for write operations you need a Keypair or wallet-adapter wallet
- Some USDC for buying deals, or a receipt token (mSOL, jitoSOL, bSOL, kUSDC, cUSDC) to list one
- Familiarity with TypeScript and Anchor is helpful but not required — the SDK hides the IDL
1. Install the SDK#
The SDK ships as @rflowdapp/rflow with three peer dependencies. Install everything in one go:
2. Read the protocol#
You can start with a read-only client — no wallet required. It exposes the on-chain config and every deal.
1import { RFlowClient } 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();8if (!config) throw new Error("Protocol not initialized");910console.log({11 feeBps: config.feeBps, // 200 = 2% protocol fee12 basePenaltyBps: config.basePenaltyBps, // 300 = 3% early-exit penalty13 minDurationDays: config.minDurationDays,14 maxDurationDays: config.maxDurationDays,15 isPaused: config.isPaused,16 totalDeals: config.dealCounter.toString(),17});If isPaused is true, the authority has frozen new deal creation — existing deals can still settle. Always check before submitting writes.
3. Browse open deals#
Available deals are deals waiting for a buyer (status = Created). The SDK filters for you:
1import { RFlowClient, formatAmount, SourceProtocol } from "@rflowdapp/rflow";2import { Connection } from "@solana/web3.js";34const client = RFlowClient.readOnly(5 new Connection("https://api.mainnet-beta.solana.com")6);78// All deals waiting for a buyer (status === Created)9const available = await client.yieldDeals.getAvailableDeals();1011for (const deal of available) {12 console.log({13 dealId: deal.dealId,14 seller: deal.seller.toBase58(),15 sellingPrice: formatAmount(deal.sellingPrice, 6) + " USDC",16 expectedYield: formatAmount(deal.expectedYield, 6) + " USDC",17 durationDays: deal.durationDays,18 protocol: SourceProtocol[deal.sourceProtocol as number],19 });20}2122// Meteora LP deals are exposed under a separate sub-client23const meteora = await client.meteoraDeals.getAvailableDeals();24console.log(`Meteora LP deals open: ${meteora.length}`);4. List your first deal#
To list a deal you need a signing wallet. The example below uses a generated Keypair for clarity — in a real app use @solana/wallet-adapter or your own key management.
One-time whitelist check
config.allowedMints. If you want a new mint listed, see the integration guide.1import {2 RFlowClient,3 SourceProtocol,4 KNOWN_MINTS,5} from "@rflowdapp/rflow";6import { Wallet } from "@coral-xyz/anchor";7import {8 Connection,9 Keypair,10 Transaction,11 sendAndConfirmTransaction,12} from "@solana/web3.js";1314const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");15const keypair = Keypair.fromSecretKey(/* your secret key */ new Uint8Array());16const wallet = new Wallet(keypair);1718const client = new RFlowClient({ connection, wallet });1920// Sell 10 mSOL of future appreciation for 90 days21const ixs = await client.yieldDeals.createDeal({22 receiptTokenMint: KNOWN_MINTS.MSOL, // mSoLzYC...m7So23 receiptTokensAmount: 10_000_000_000, // 10 mSOL (9 decimals)24 principalValueAtLock: 1_180_000_000, // ~$1,180 USDC value at lock25 expectedYield: 17_000_000, // ~$17 expected yield (90d @ ~6%)26 sellingPrice: 14_000_000, // List for $14 — buyer’s discount27 durationDays: 90, // 30 / 60 / 90 / 180 / 36528 sourceProtocol: SourceProtocol.Marinade,29 exchangeRateAtLock: 1_180_000, // 1.18 mSOL/SOL scaled 1e630 // On mainnet for LSTs, pass the Pyth price update account:31 // priceUpdate: msolUsdPriceUpdatePubkey,32});3334const tx = new Transaction().add(...ixs);35const sig = await sendAndConfirmTransaction(connection, tx, [keypair]);36console.log("Deal listed:", sig);3738const config = await client.getConfig();39const dealId = (config!.dealCounter as unknown as number) - 1;40const created = await client.yieldDeals.getDeal(dealId);41console.log("Deal PDA:", created?.pda.toBase58());Where exchangeRateAtLock comes from
principal_value_at_lock / receipt_tokens_amount scaled by 1e6. The program snapshots this so settlement can detect oracle manipulation. The SDK validates the math; it does not fetch the rate for you.5. Fill a deal as a buyer#
Filling a deal is a single instruction. The SDK fetches the seller and payment mint from the on-chain account for you — you only need the seller's USDC ATA and the protocol treasury USDC ATA:
1import { RFlowClient } from "@rflowdapp/rflow";2import { getAssociatedTokenAddress } from "@solana/spl-token";3import { Connection, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";45// Assume `wallet`, `keypair`, and `client` are already set up as above6const dealId = 42;7const deal = await client.yieldDeals.getDeal(dealId);8if (!deal) throw new Error("Deal not found");910const sellerPaymentAccount = await getAssociatedTokenAddress(11 deal.paymentMint,12 deal.seller13);1415const config = await client.getConfig();16const treasuryAccount = await getAssociatedTokenAddress(17 deal.paymentMint,18 config!.treasury19);2021const ixs = await client.yieldDeals.buyDeal(22 dealId,23 sellerPaymentAccount,24 treasuryAccount25);2627const tx = new Transaction().add(...ixs);28const sig = await sendAndConfirmTransaction(connection, tx, [keypair]);29console.log("Deal filled:", sig);That’s the full loop
ends_at is permissionless — anyone (including a keeper) can call settle_deal. The yield lands in the buyer's token account; the principal returns to the seller.