dynamic-bonding-curve-sdk examples and tests. They focus on transaction construction patterns. For the full method catalog, see the SDK Reference.
Setup
import BN from "bn.js";
import { NATIVE_MINT } from "@solana/spl-token";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import {
ActivationType,
BaseFeeMode,
buildCurveWithCustomSqrtPrices,
CollectFeeMode,
createSqrtPrices,
DammV2BaseFeeMode,
DammV2DynamicFeeMode,
deriveDbcPoolAddress,
DynamicBondingCurveClient,
getSwapQuoteTransferFees,
MigratedCollectFeeMode,
MigrationFeeOption,
MigrationOption,
SwapMode,
TokenDecimal,
TokenType,
TokenAuthorityOption,
} from "@meteora-ag/dynamic-bonding-curve-sdk";
const connection = new Connection(process.env.RPC_URL!, "confirmed");
const client = new DynamicBondingCurveClient(connection, "confirmed");
Build A Curve Config
buildCurveWithCustomSqrtPrices gives explicit control over the curve checkpoints. Other supported helpers are listed in the SDK Reference.
const sqrtPrices = createSqrtPrices(
[0.000000001, 0.00000000105, 0.000000002, 0.000001],
TokenDecimal.SIX,
TokenDecimal.NINE
);
const curveConfig = buildCurveWithCustomSqrtPrices({
token: {
tokenType: TokenType.SPLToken,
tokenBaseDecimal: TokenDecimal.SIX,
tokenQuoteDecimal: TokenDecimal.NINE,
tokenAuthorityOption: TokenAuthorityOption.PartnerUpdateAuthority,
totalTokenSupply: 1_000_000_000,
leftover: 1_000,
},
fee: {
baseFeeParams: {
baseFeeMode: BaseFeeMode.FeeSchedulerExponential,
feeSchedulerParam: {
startingFeeBps: 9000,
endingFeeBps: 120,
numberOfPeriod: 60,
totalDuration: 60,
},
},
dynamicFeeEnabled: true,
collectFeeMode: CollectFeeMode.QuoteToken,
creatorTradingFeePercentage: 0,
poolCreationFee: 1,
enableFirstSwapWithMinFee: false,
},
migration: {
migrationOption: MigrationOption.MET_DAMM_V2,
migrationFeeOption: MigrationFeeOption.Customizable,
migrationFee: {
feePercentage: 10,
creatorFeePercentage: 50,
},
migratedPoolFee: {
collectFeeMode: MigratedCollectFeeMode.QuoteToken,
dynamicFee: DammV2DynamicFeeMode.Enabled,
poolFeeBps: 120,
baseFeeMode: DammV2BaseFeeMode.FeeTimeSchedulerLinear,
},
},
liquidityDistribution: {
partnerLiquidityPercentage: 0,
partnerPermanentLockedLiquidityPercentage: 100,
creatorLiquidityPercentage: 0,
creatorPermanentLockedLiquidityPercentage: 0,
},
lockedVesting: {
totalLockedVestingAmount: 0,
numberOfVestingPeriod: 0,
cliffUnlockAmount: 0,
totalVestingDuration: 0,
cliffDurationFromMigrationTime: 0,
},
activationType: ActivationType.Timestamp,
sqrtPrices,
liquidityWeights: [2, 1, 1],
});
Create A Config
const partner = Keypair.generate();
const config = Keypair.generate();
const createConfigTx = await client.partner.createConfig2({
config: config.publicKey,
feeClaimer: partner.publicKey,
leftoverReceiver: partner.publicKey,
payer: partner.publicKey,
quoteMint: NATIVE_MINT,
...curveConfig,
});
createConfigTx.feePayer = partner.publicKey;
config signer.
Create A Pool
const creator = Keypair.generate();
const baseMint = Keypair.generate();
const createPoolTx = await client.creator.createPool({
baseMint: baseMint.publicKey,
config: config.publicKey,
name: "TEST",
symbol: "TEST",
uri: "https://example.com/token.json",
payer: creator.publicKey,
poolCreator: creator.publicKey,
});
createPoolTx.feePayer = creator.publicKey;
const pool = deriveDbcPoolAddress(
NATIVE_MINT,
baseMint.publicKey,
config.publicKey
);
Quote And Swap
const poolState = await client.state.getPool(pool);
const configState = await client.state.getPoolConfig(poolState.poolState.config);
const currentPoint =
configState.activationType === ActivationType.Slot
? new BN(await connection.getSlot())
: new BN(Math.floor(Date.now() / 1000));
const transferFees = await getSwapQuoteTransferFees(connection, configState, {
baseMint: poolState.poolState.baseMint,
});
const quote = client.pool.swapQuote2({
virtualPool: poolState,
config: configState,
swapBaseForQuote: false,
swapMode: SwapMode.ExactIn,
amountIn: new BN(1_000_000_000),
slippageBps: 100,
hasReferral: false,
eligibleForFirstSwapWithMinFee: false,
currentPoint,
...transferFees,
});
// quote.includedTransferFeeAmountIn is what the user pays.
// quote.excludedTransferFeeAmountOut is what the user receives after a Token 2022 transfer fee.
const swapTx = await client.pool.swap2({
owner: new PublicKey("USER_WALLET"),
payer: new PublicKey("FEE_PAYER"),
pool,
swapBaseForQuote: false,
swapMode: SwapMode.ExactIn,
amountIn: new BN(1_000_000_000),
minimumAmountOut: quote.minimumAmountOut,
referralTokenAccount: null,
});
Partial Fill Near Completion
Use partial fill when the final buy may cross the migration threshold before consuming the full input amount.const partialQuote = client.pool.swapQuote2({
virtualPool: poolState,
config: configState,
swapBaseForQuote: false,
swapMode: SwapMode.PartialFill,
amountIn: new BN(5_000_000_000),
slippageBps: 100,
hasReferral: false,
eligibleForFirstSwapWithMinFee: false,
currentPoint,
});
const partialFillTx = await client.pool.swap2({
owner: new PublicKey("USER_WALLET"),
pool,
swapBaseForQuote: false,
swapMode: SwapMode.PartialFill,
amountIn: new BN(5_000_000_000),
minimumAmountOut: partialQuote.minimumAmountOut,
referralTokenAccount: null,
});
Read Progress And Fees
const quoteProgress = await client.state.getPoolQuoteTokenCurveProgress(pool);
const baseProgress = await client.state.getPoolBaseTokenCurveProgress(pool);
const feeMetrics = await client.state.getPoolFeeMetrics(pool);
const feeBreakdown = await client.state.getPoolFeeBreakdown(pool);
console.log({
quoteProgress,
baseProgress,
currentPartnerQuoteFee: feeMetrics.current.partnerQuoteFee.toString(),
totalTradingQuoteFee: feeMetrics.total.totalTradingQuoteFee.toString(),
partnerUnclaimedQuoteFee: feeBreakdown.partner.unclaimedQuoteFee.toString(),
});
Claim Fees
const partnerClaimTx = await client.partner.claimPartnerTradingFeeToReceiver({
feeClaimer: partner.publicKey,
payer: partner.publicKey,
pool,
maxBaseAmount: new BN("18446744073709551615"),
maxQuoteAmount: new BN("18446744073709551615"),
receiver: partner.publicKey,
});
const creatorClaimTx = await client.creator.claimCreatorTradingFeeToReceiver({
creator: creator.publicKey,
payer: creator.publicKey,
pool,
maxBaseAmount: new BN("18446744073709551615"),
maxQuoteAmount: new BN("18446744073709551615"),
receiver: creator.publicKey,
});
Migration Flow
| Destination | SDK sequence |
|---|---|
| DAMM v1 | createDammV1MigrationMetadata, optional createLocker, migrateToDammV1, optional lockDammV1LpToken, optional claimDammV1LpToken. |
| DAMM v2 | Optional createLocker, then migrateToDammV2. |
const { transaction: migrateToDammV2Tx } = await client.migration.migrateToDammV2({
payer: new PublicKey("MIGRATOR"),
pool,
dammConfig: new PublicKey("DAMM_V2_CONFIG"),
});
Meteora runs mainnet migration keepers for eligible pools, including matching quote-mint thresholds, Stock Token quote pairs, and Jupiter Verified quote tokens with Organic Score greater than 50 and notional value greater than 750 USD. The SDK migration builders are still useful for manual migration, devnet validation, and custom keeper infrastructure. See DBC Migration and Liquidity for the full keeper criteria.

