DeFi Basics on Solana
8 examples to get you started with DeFi integrations - 5 basic and 3 intermediate. Covers swaps, AMMs, lending, LSTs, oracles, and composable CPI flows on Agave 4.1.1.
Prerequisites
- Solana CLI 3.0.10 on devnet for balance and token checks.
@solana/kit7.0.0 for TypeScript client examples.- Familiarity with CPI Basics - most DeFi actions are CPI chains.
npm install @solana/kit@7.0.0 @solana/spl-tokenBasic Examples
1. Read a Token Balance Before a Swap
Every DeFi action starts with knowing what the user holds.
import { createSolanaRpc, address } from "@solana/kit";
import { fetchMint, fetchToken } from "@solana-program/token";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const owner = address("YOUR_WALLET_PUBKEY");
const mint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC
const atas = await rpc.getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }).send();
console.log(atas.value);- Balances live in SPL Token accounts, not the wallet pubkey directly.
- Always resolve the ATA (associated token account) for a mint before building swap instructions.
- Use
jsonParsedto avoid manual layout decoding in clients.
Related: Swaps with Jupiter - routing after balance check
2. Quote a Swap via Jupiter API
Aggregators return a route and serialized transaction bytes.
const inputMint = "So11111111111111111111111111111111111111112"; // wSOL
const outputMint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; // USDC
const amount = "100000000"; // 0.1 SOL in lamports
const quoteRes = await fetch(
`https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${amount}&slippageBps=50`
);
const quote = await quoteRes.json();
console.log(quote.outAmount, quote.routePlan);slippageBpsis basis points (50 = 0.5%).- The quote includes
routePlanshowing which AMM legs execute. - Never hard-code pool addresses - routes change as liquidity shifts.
Related: AMMs (Raydium, Orca, Meteora) - what Jupiter routes through
3. Understand Constant-Product Pool Math
AMMs on Solana use the same x * y = k invariant as EVM DEXes, but state lives in program-owned accounts.
// Simplified constant-product output (fee omitted)
pub fn amount_out(amount_in: u64, reserve_in: u64, reserve_out: u64) -> u64 {
let numerator = (amount_in as u128) * (reserve_out as u128);
let denominator = (reserve_in as u128) + (amount_in as u128);
(numerator / denominator) as u64
}- Reserves are read from on-chain pool accounts at transaction build time.
- Stale reserves cause failed transactions or bad fills - refresh before send.
- Use checked math in programs; client previews can use u128 intermediates.
Related: AMMs (Raydium, Orca, Meteora) - pool layouts
4. Check an Oracle Price Feed
Lending and perps depend on fresh oracle prices.
import { createSolanaRpc, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
// Pyth SOL/USD price feed account (mainnet)
const priceAccount = address("H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG");
const account = await rpc.getAccountInfo(priceAccount, { encoding: "base64" }).send();
console.log(account.value?.data);- Oracle accounts store price, confidence, and publish slot.
- Programs must check
publish_slotstaleness against a max age. - Never trust client-side price reads for on-chain settlement.
Related: Oracles - Pyth and Switchboard integration
5. Wrap SOL to wSOL for AMM Swaps
Most pools quote against wrapped SOL, not native lamports in the wallet.
spl-token wrap 0.5
spl-token accountsspl-token wrapcreates/funds the wSOL ATA and syncs native balance.- Close the wSOL ATA after swaps to reclaim rent if you no longer need it.
- Jupiter routes often auto-handle wSOL, but manual CPI integrations must not.
Related: Swaps with Jupiter - wSOL in route legs
Intermediate Examples
6. Build and Send a Jupiter Swap Transaction
Turn a quote into a signed versioned transaction.
import { createSolanaRpc, createSolanaRpcSubscriptions, address } from "@solana/kit";
import { getBase58Codec } from "@solana/codecs-strings";
const swapRes = await fetch("https://quote-api.jup.ag/v6/swap", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
quoteResponse: quote,
userPublicKey: "YOUR_WALLET_PUBKEY",
wrapAndUnwrapSol: true,
dynamicComputeUnitLimit: true,
}),
});
const { swapTransaction } = await swapRes.json();
const txBytes = getBase58Codec().decode(swapTransaction);
// Sign and send with your wallet signerdynamicComputeUnitLimitsimulates CU usage and sets a safe budget.wrapAndUnwrapSolhandles wSOL lifecycle for SOL-involved routes.- Simulate on devnet or Surfpool 0.12.0 before mainnet submission.
Related: DeFi Integrations Best Practices - slippage and simulation policy
7. Compose Lending CPI with Collateral Oracle
Money markets read oracle prices inside the borrow instruction.
// Anchor-style account validation sketch
#[account(
constraint = oracle.key() == market.oracle_pubkey @ ErrorCode::BadOracle,
)]
pub oracle: AccountInfo<'info>,- Lending programs CPI into oracle programs or read pre-loaded oracle accounts.
- Health factor checks must use the same price the liquidation engine uses.
- Test oracle staleness paths in LiteSVM 0.6.x before mainnet.
Related: Lending & Borrowing - Kamino and MarginFi patterns
8. Route Through an LST for Staking Yield
Liquid staking tokens trade like any SPL mint but represent staked SOL.
const jitoSolMint = "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn";
// Swap SOL -> jitoSOL via Jupiter, then deposit jitoSOL as collateral- LSTs accrue yield via exchange rate, not rebasing balances.
- Integrations must use the correct mint (jitoSOL, mSOL, bSOL) per protocol allowlist.
- Sanctum routes unify LST liquidity across providers.
Related: Liquid Staking (LSTs) - mint addresses and rates
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.