Skip to main content
Set up the Exponent SDK and walk through the complete CLMM lifecycle — from buying and selling PT/YT, to providing liquidity, withdrawing, and reading market state.

Installation

yarn add @exponent-labs/exponent-sdk

Setup

import { MarketThree, LOCAL_ENV } from "@exponent-labs/exponent-sdk";
import {
  Connection,
  PublicKey,
  Transaction,
  sendAndConfirmTransaction,
  Keypair,
} from "@solana/web3.js";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const wallet = Keypair.fromSecretKey(/* your keypair */);
const marketAddress = new PublicKey("...");

const market = await MarketThree.load(LOCAL_ENV, connection, marketAddress);

1. Buy PT

Use ixWrapperBuyPt to buy PT (Principal Tokens) using base assets. This wraps the base into SY and swaps SY for PT in a single atomic operation.
const { ixs, setupIxs } = await market.ixWrapperBuyPt({
  owner: wallet.publicKey,
  minPtOut: 1_000_000_000n,   // minimum PT to receive
  baseIn: 1_100_000_000n,     // base asset to spend
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet]);

2. Sell PT

Use ixWrapperSellPt to sell PT back for base assets. This swaps PT for SY and redeems SY for base in one transaction.
const { ixs, setupIxs } = await market.ixWrapperSellPt({
  owner: wallet.publicKey,
  amount: 1_000_000_000n,     // PT to sell
  minBaseOut: 900_000_000n,   // minimum base to receive
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet]);

3. Buy YT

Use ixWrapperBuyYt to buy YT (Yield Tokens) using base assets. This wraps base into SY, strips SY into PT and YT, and sells the excess PT back — all atomically.
const { ixs, setupIxs } = await market.ixWrapperBuyYt({
  owner: wallet.publicKey,
  ytOut: 1_000_000_000n,      // YT to receive
  maxBaseIn: 1_200_000_000n,  // maximum base to spend
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet]);

4. Sell YT

Use ixWrapperSellYt to sell YT back for base assets. This buys PT with SY, merges PT and YT back into SY, and redeems SY for base.
const { ixs, setupIxs } = await market.ixWrapperSellYt({
  owner: wallet.publicKey,
  amount: 1_000_000_000n,     // YT to sell
  minBaseOut: 900_000_000n,   // minimum base to receive
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet]);

5. Provide Liquidity

Use ixWrapperProvideLiquidity to provide liquidity from base assets within a specified APY range. This wraps base into SY, strips into PT and YT, adds PT liquidity, and returns YT and LP tokens.
const { ixs, setupIxs, signers } = await market.ixWrapperProvideLiquidity({
  depositor: wallet.publicKey,
  amountBase: 10_000_000_000n,  // base asset to deposit
  minLpOut: 9_000_000_000n,     // minimum LP tokens to receive
  lowerTickApy: 0.05,           // 5% APY lower bound
  upperTickApy: 0.15,           // 15% APY upper bound
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet, ...signers]);
The signers array contains the generated LP position keypair. You must include it when signing the transaction, otherwise the transaction will fail.
The lowerTickApy and upperTickApy parameters are decimal numbers — use 0.05 for 5% APY, not 500 or 50000.

6. Withdraw Liquidity

Use ixWithdrawLiquidityToBase to withdraw an LP position back to base assets. You need the LP position’s public key, which you can obtain from getUserLpPositions.
const { ixs, setupIxs } = await market.ixWithdrawLiquidityToBase({
  owner: wallet.publicKey,
  amountLp: 9_000_000_000n,        // LP tokens to withdraw
  minBaseOut: 8_500_000_000n,       // minimum base to receive
  lpPosition: lpPositionPublicKey,  // from getUserLpPositions
});

const tx = new Transaction().add(...setupIxs, ...ixs);
await sendAndConfirmTransaction(connection, tx, [wallet]);
The lpPosition parameter is the public key of your LP position account. See step 7 to learn how to retrieve it.

7. Check LP Positions

Use getUserLpPositions to retrieve all LP positions for a wallet in a specific market.
const { lpPositions } = await market.getUserLpPositions(
  wallet.publicKey,
  marketAddress,
);

for (const positions of lpPositions) {
  for (const pos of positions) {
    console.log("Position:", pos.publicKey.toBase58());
    console.log("LP Balance:", pos.account.lpBalance);
    console.log("Lower Tick:", pos.account.lowerTickIdx);
    console.log("Upper Tick:", pos.account.upperTickIdx);
    console.log("Unclaimed PT fees:", pos.account.tokensOwedPt);
    console.log("Unclaimed SY fees:", pos.account.tokensOwedSy);
  }
}

8. Calculate Withdrawal Amounts

Use getPtAndSyOnWithdrawLiquidity to preview how much PT and SY you would receive when withdrawing a position.
const result = market.getPtAndSyOnWithdrawLiquidity(pos.account);

console.log("PT to receive:", result.totalPtOut);
console.log("SY to receive:", result.totalSyOut);
You can pass an optional second parameter to calculate a partial withdrawal: market.getPtAndSyOnWithdrawLiquidity(position, partialLpAmount).

9. Reading Market State

The MarketThree instance exposes getter properties for reading onchain state:
// Token mints
console.log("PT Mint:", market.mintPt.toBase58());
console.log("SY Mint:", market.mintSy.toBase58());
console.log("YT Mint:", market.mintYt.toBase58());

// Market balances
console.log("PT Balance:", market.ptBalance);
console.log("SY Balance:", market.syBalance);
console.log("LP Balance:", market.lpBalance);

// Market parameters
console.log("SY Exchange Rate:", market.currentSyExchangeRate);
console.log("Seconds Remaining:", market.secondsRemaining);
console.log("Status Flags:", market.statusFlags);

Next Steps