Dynamic AMM v2 (DAMM v2) is a brand new constant-product AMM program for liquidity pools, with a set of features to optimize transaction fees and provide greater flexibility for liquidity providers, launchpads, and token launches.

DAMM v2 Launch Pools can also be created with an Alpha Vault as part of the token launch process. This is optional.

Below we provide the steps to use Bun to configure and run sample scripts to conveniently set up a DAMM v2 Launch Pool on your own.

Getting Started

Dependencies

Install Bun: You’ll also need bun to run the scripts. Install it via bun installation. Then install the dependencies by running the command bun install

Scripts

The following code examples can be used to initialize and seed a DAMM v2 pool on Meteora. It allows you to configure parameters for the pool, including fees, pricing, and activation conditions, before executing initialization and seeding commands.

1. Create customizable DAMM v2 pool

Clone repo: https://github.com/MeteoraAg/meteora-pool-setup

Select an example config to reference and change the parameters inside according to your needs: https://github.com/MeteoraAg/meteora-pool-setup/blob/main/config/create_damm_v2_customize_pool_default_dynamic_fee.json

Run the script with the config file which is updated with your preferred parameters.

For example, run:

bun run src/create_damm_v2_customizable_pool.ts --config ./config/create_damm_v2_customize_pool.json

Replace ./config/create_damm_v2_customize_pool.json with the path to your config file, after you have set your config parameters.

After deployment

To view pool on the UI, access the link below

For DAMM v2 pool: https://app.meteora.ag/pools/`POOL_ADDRESS`

Configuration Details

There are various config files that can be found in the config directory.

It contains all the configurations required to run the scripts. We also need to provide the keypair for the payer wallet in keypair.json file.

General configuration

  • rpcUrl: Solana RPC URL to get data and send transactions.

  • keypairFilePath: Keypair file path to send transactions.

  • dryRun: Set to false to send transactions.

  • computeUnitPriceMicroLamports: CU price in micro lamports unit. For example: 100000.

  • createBaseToken: Configuration to create base token.

  • baseMint: Base token address if the createBaseToken field is not set.

  • quoteSymbol: Quote token symbol, only SOL or USDC is supported.

  • quoteMint: Quote token mint, in case the user wants to create a DLMM launch pool with a token other than SOL or USDC.

  • dynamicAmm: Dynamic AMM pool configuration.

  • dynamicAmmV2: Dynamic AMM V2 pool configuration.

  • dlmm: DLMM pool configuration.

  • alphaVault: Fcfs or Prorata Alpha Vault configuration.

Some configuration constraints:

  • createBaseToken and baseMint cannot be used together.

  • dynamicAmm and dlmm cannot be used together.

Create Base Token configuration

  • mintBaseTokenAmount: Base token amount to be minted.

  • baseDecimals: Base token decimal.

Dynamic AMM v2 configuration

  • baseAmount: Base token amount.

  • quoteAmount: Quote token amount (Nullable value)

  • initPrice: Initial price for the pool

  • maxPrice: Max price range setup (null to use default)

  • poolFees:

    • maxBaseFeeBps: Starting fee in basis points (e.g., 25 = 0.25%). It is base fee if scheduler is not set.

    • minBaseFeeBps: Target fee after reduction periods. Should be equal maxBaseFeeBps when fee scheduler is not used.

    • numberOfPeriod: Number of periods for fee reduction schedule

    • totalDuration: Total duration of the fee schedule (in slots or seconds based on activation type). totalDuration == 0 if the FeeScheduler not be set up.

    • feeSchedulerMode: Fee scheduler mode (0 = linear, 1 = exponential)

    • useDynamicFee: Whether to use dynamic fee calculation based on price volatility (true/false)

    • dynamicFeeConfig: Configuration when useDynamicFee is true if not provide will use as default params in scripts

      • filterPeriod: Period for filtering price updates

      • decayPeriod: Period for decaying volatility accumulator

      • reductionFactor: Factor for reducing the volatility impact

      • variableFeeControl: Parameter controlling the variable fee response

      • maxVolatilityAccumulator: Maximum value for the volatility accumulator

    • collectFeeMode: Fee collection mode (0 = base + quote, 1 = only quote)

  • activationType: To activate pool trading base on slot or timestamp.

  • activationPoint: To activate pool trading at a point, either slot value or timestamp value base on activationType.

  • hasAlphaVault: Whether alpha vault is enabled or not for this pool.

Alpha Vault configuration

  • poolType: dynamic or dlmm pool type.

  • alphaVaultType: Alpha Vault type, could be fcfs or prorata

  • depositingPoint: Absolute value that, the slot or timestamp that allows deposit depend on the pool activation type.

  • startVestingPoint: Absolute value, the slot or timestamp that start vesting depend on the pool activation type.

  • endVestingPoint: Absolute value, the slot or timestamp that end vesting depend on the pool activation type.

  • maxDepositCap: Maximum deposit cap.

  • individualDepositingCap: Individual deposit cap.

  • escrowFee: Fee to create stake escrow account.

  • whitelistMode: permissionless or permission_with_merkle_proof or permission_with_authority.

Pro rata configuration

  • depositingPoint: Absolute value that, the slot or timestamp that allows deposit depend on the pool activation type.

  • startVestingPoint: Absolute value, the slot or timestamp that start vesting depend on the pool activation type.

  • endVestingPoint: Absolute value, the slot or timestamp that end vesting depend on the pool activation type.

  • maxBuyingCap: Maximum buying cap.

  • escrowFee: Fee to create stake escrow account.

  • whitelistMode: permissionless or permission_with_merkle_proof or permission_with_authority.

Technical FAQ

Please read this section for additional technical considerations and troubleshooting.


Alternative: Using TypeScript SDK to create a DAMM v2 Pool

Getting Started

The TypeScript SDK provides a set of tools and methods to interact with the Meteora Dynamic CP-AMM (DAMM v2) program.

Below you can find the main functions for creating a new pool:

  • createPool
  • createCustomPool
  • createCustomPoolWithDynamicConfig
  • createPosition
  • addLiquidity

1. Install dependencies and initialize instance

1.1 Install

pnpm install @meteora-ag/cp-amm-sdk
# or
yarn add @meteora-ag/cp-amm-sdk

1.2 Initialization

import { Connection } from "@solana/web3.js";
import { CpAmm } from "@meteora-ag/cp-amm-sdk";

// Initialize a connection to the Solana network
const connection = new Connection("https://api.mainnet-beta.solana.com");

// Create a new instance of the CpAmm SDK
const cpAmm = new CpAmm(connection);

1.3 Test

pnpm install
pnpm test

1.4 Faucets

https://faucet.raccoons.dev/

2. Core Functions and Use Cases

createPool

Creates a new standard pool according to a predefined configuration.

Function

async createPool(params: CreatePoolParams): TxBuilder

Parameters

interface CreatePoolParams {
  payer: PublicKey;              // The wallet paying for the transaction
  creator: PublicKey;            // The creator of the pool
  config: PublicKey;             // The configuration account for the pool
  positionNft: PublicKey;        // The mint for the initial position NFT
  tokenAMint: PublicKey;         // The mint address for token A
  tokenBMint: PublicKey;         // The mint address for token B
  activationPoint: BN;           // 0: slot, 1: timestamp
  tokenAAmount: BN;              // Initial amount of token A to deposit
  tokenBAmount: BN;              // Initial amount of token B to deposit
  minSqrtPrice: BN;              // Minimum sqrt price (typically MIN_SQRT_PRICE)
  maxSqrtPrice: BN;              // Maximum sqrt price (typically MAX_SQRT_PRICE)
  tokenADecimal: number;         // Decimal places for token A
  tokenBDecimal: number;         // Decimal places for token B
  tokenAProgram: PublicKey;      // Token program for token A
  tokenBProgram: PublicKey;      // Token program for token B
}

Returns

A transaction builder (TxBuilder) that can be used to build, sign, and send the transaction.

Example

const createPoolTx = await cpAmm.createPool({
  payer: wallet.publicKey,
  creator: wallet.publicKey,
  config: configAddress,
  positionNft: positionNftMint,
  tokenAMint: usdcMint,
  tokenBMint: solMint,
  activationPoint: new BN(Date.now()),
  tokenAAmount: new BN(1_000_000_000), // 1,000 USDC with 6 decimals
  tokenBAmount: new BN(5_000_000_000), // 5 SOL with 9 decimals
  minSqrtPrice: MIN_SQRT_PRICE,
  maxSqrtPrice: MAX_SQRT_PRICE,
  tokenADecimal: 6,
  tokenBDecimal: 9,
  tokenAProgram: TOKEN_PROGRAM_ID,
  tokenBProgram: TOKEN_PROGRAM_ID
});

const tx = await createPoolTx.transaction();
const signature = await wallet.sendTransaction(tx, connection);

Notes

  • Both token amounts must be greater than zero
  • If using native SOL, it will be automatically wrapped to wSOL
  • The config parameter should be a valid configuration account
  • Pool creation automatically creates an initial position
  • To set your Pool and Fee Config, please read the instructions in Setting Pool and Fee Config for DAMM v2

createCustomPool

Creates a customizable pool with specific fee parameters, reward settings, and activation conditions.

Function

async createCustomPool(params: InitializeCustomizeablePoolParams): Promise<{
  tx: Transaction;
  pool: PublicKey;
  position: PublicKey;
}>

Parameters

interface InitializeCustomizeablePoolParams {
  payer: PublicKey;              // The wallet paying for the transaction
  creator: PublicKey;            // The creator of the pool
  positionNft: PublicKey;        // The mint for the initial position NFT
  tokenAMint: PublicKey;         // The mint address for token A
  tokenBMint: PublicKey;         // The mint address for token B
  tokenAAmount: BN;              // Initial amount of token A to deposit
  tokenBAmount: BN;              // Initial amount of token B to deposit
  minSqrtPrice: BN;              // Minimum sqrt price
  maxSqrtPrice: BN;              // Maximum sqrt price
  tokenADecimal: number;         // Decimal places for token A
  tokenBDecimal: number;         // Decimal places for token B
  poolFees: PoolFees;            // Fee configuration
  hasAlphaVault: boolean;        // Whether the pool has an alpha vault
  collectFeeMode: number;        // How fees are collected (0: normal, 1: alpha)
  activationPoint: BN;           // The slot or timestamp for activation
  activationType: number;        // 0: slot, 1: timestamp
  tokenAProgram: PublicKey;      // Token program for token A
  tokenBProgram: PublicKey;      // Token program for token B
}

interface PoolFees {
  baseFee: {
    feeSchedulerMode: number;  // 0: Linear, 1: Exponential
    cliffFeeNumerator: number; 
    numberOfPeriod: number;   
    reductionFactor: number; 
    periodFrequency: number;  
  };
  partnerFee?: {                
    partnerAddress: PublicKey;  
    partnerFeeNumerator: number;
  };
  dynamicFee?: {              
    initialized: boolean;     
    volatilityAccumulator?: number; 
    binStep?: number;        
    variableFeeControl?: {    
      maxFeeNumerator: number;
      minFeeNumerator: number;
      volatilityThreshold: number;
      feeDamper: number;   
    };
  };
}

Returns

An object containing:

  • tx: The transaction to sign and send
  • pool: The public key of the created pool
  • position: The public key of the initial position

Example

const poolFees = {
  baseFee: {
    feeSchedulerMode: 0, // 0: Linear, 1: Exponential
    cliffFeeNumerator: 1_000_000,
    numberOfPeriod: 0,
    reductionFactor: 0,
    periodFrequency: 0
  },
  partnerFee: {
    partnerAddress: partnerWallet.publicKey,
    partnerFeeNumerator: 1000,
  },
  dynamicFee: {
    initialized: false
  }
};

const { tx, pool, position } = await cpAmm.createCustomPool({
  payer: wallet.publicKey,
  creator: wallet.publicKey,
  positionNft: positionNftMint,
  tokenAMint: usdcMint,
  tokenBMint: btcMint,
  tokenAAmount: new BN(5_000_000_000),
  tokenBAmount: new BN(20_000_000),
  minSqrtPrice: MIN_SQRT_PRICE,
  maxSqrtPrice: MAX_SQRT_PRICE,
  tokenADecimal: 6,
  tokenBDecimal: 8,
  poolFees,
  hasAlphaVault: false,
  collectFeeMode: 0, // 0: BothToken, 1: onlyB
  activationPoint: new BN(Date.now()),
  activationType: 1, // 0: slot, 1: timestamp
  tokenAProgram: TOKEN_PROGRAM_ID,
  tokenBProgram: TOKEN_PROGRAM_ID
});

const signature = await wallet.sendTransaction(tx, connection);

Notes

  • Use this function instead of createPool when you need custom fee structures
  • Dynamic fees can adjust based on market volatility
  • Partner fees allow a portion of trading fees to be directed to a specific account
  • Alpha vault is an advanced feature for protocol-owned liquidity

createCustomPoolWithDynamicConfig

Creates a customizable pool with dynamic configuration, allowing for specific fee parameters with specified pool creator authority.

Function

async createCustomPoolWithDynamicConfig(params: InitializeCustomizeablePoolWithDynamicConfigParams): Promise<{
  tx: Transaction;
  pool: PublicKey;
  position: PublicKey;
}>

Parameters

interface InitializeCustomizeablePoolWithDynamicConfigParams {
  payer: PublicKey;              // The wallet paying for the transaction
  creator: PublicKey;            // The creator of the pool
  positionNft: PublicKey;        // The mint for the initial position NFT
  tokenAMint: PublicKey;         // The mint address for token A
  tokenBMint: PublicKey;         // The mint address for token B
  tokenAAmount: BN;              // Initial amount of token A to deposit
  tokenBAmount: BN;              // Initial amount of token B to deposit
  sqrtMinPrice: BN;              // Minimum sqrt price
  sqrtMaxPrice: BN;              // Maximum sqrt price
  initSqrtPrice: BN;             // Initial sqrt price in Q64 format
  liquidityDelta: BN;            // Initial liquidity in Q64 format
  poolFees: PoolFeesParams;      // Fee configuration
  hasAlphaVault: boolean;        // Whether the pool has an alpha vault
  collectFeeMode: number;        // How fees are collected (0: normal, 1: alpha)
  activationPoint: BN | null;    // The slot or timestamp for activation (null for immediate)
  activationType: number;        // 0: slot, 1: timestamp
  tokenAProgram: PublicKey;      // Token program for token A
  tokenBProgram: PublicKey;      // Token program for token B
  config: PublicKey;             // dynamic config account
  poolCreatorAuthority: PublicKey; // Authority allowed to create pools with this config
}

Returns

An object containing:

  • tx: The transaction to sign and send
  • pool: The public key of the created pool
  • position: The public key of the initial position

Example

// First, prepare the pool creation parameters
const tokenAAmount = new BN(5_000_000_000);
const tokenBAmount = new BN(20_000_000);
const sqrtPrice = getSqrtPriceFromPrice("172", tokenADecimal, tokenBDecimal);
const sqrtMinPrice = getSqrtPriceFromPrice("4", tokenADecimal, tokenBDecimal);
const sqrtMaxPrice = getSqrtPriceFromPrice("400", tokenADecimal, tokenBDecimal);
const { initSqrtPrice, liquidityDelta } = cpAmm.getLiquidityDelta({
  maxAmountTokenA: tokenAAmount,
  maxAmountTokenB: tokenBAmount,
  sqrtMaxPrice,
  sqrtMinPrice,
  sqrtPrice,
});

const baseFeeParams = getBaseFeeParams(25, 25, FeeSchedulerMode.Linear, 0, 0); // base fee: 0.25%
const dynamicFeeParams = getDynamicFeeParams(25); // max dynamic fee 0.25%
const poolFees: PoolFeesParams = {
    baseFee: baseFeeParams,
    protocolFeePercent: 20,
    partnerFeePercent: 0,
    referralFeePercent: 20,
    dynamicFee: dynamicFeeParams,
  };

const { tx, pool, position } = await cpAmm.createCustomPoolWithDynamicConfig({
  payer: wallet.publicKey,
  creator: wallet.publicKey,
  config: dynamicConfigAddress,
  poolCreatorAuthority: poolCreatorAuth.publicKey,
  positionNft: positionNftMint,
  tokenAMint: usdcMint,
  tokenBMint: btcMint,
  tokenAAmount,
  tokenBAmount,
  sqrtMinPrice,
  sqrtMaxPrice,
  initSqrtPrice,
  liquidityDelta,
  poolFees,
  hasAlphaVault: false,
  collectFeeMode: 0, // 0: Both tokens, 1: Only token B
  activationPoint: null,
  activationType: 1, // 0: slot, 1: timestamp
  tokenAProgram: TOKEN_PROGRAM_ID,
  tokenBProgram: TOKEN_PROGRAM_ID,
});

createPosition

Creates a new position in an existing pool.

Function

async createPosition(params: CreatePositionParams): TxBuilder

Parameters

interface CreatePositionParams {
  owner: PublicKey;          // The owner of the position
  payer: PublicKey;          // The wallet paying for the transaction
  pool: PublicKey;           // The pool to create a position in
  positionNft: PublicKey;    // The mint for the position NFT
}

Returns

A transaction builder (TxBuilder) that can be used to build, sign, and send the transaction.

Example

const createPositionTx = await cpAmm.createPosition({
  owner: wallet.publicKey,
  payer: wallet.publicKey,
  pool: poolAddress,
  positionNft: positionNftMint
});

const tx = await createPositionTx.transaction();
const signature = await wallet.sendTransaction(tx, connection);

Notes

  • The positionNft should be a new mint that doesn’t already have a position
  • Creating a position doesn’t automatically add liquidity
  • After creating a position, use addLiquidity to provide tokens

addLiquidity

Adds liquidity to an existing position.

Function

async addLiquidity(params: AddLiquidityParams): TxBuilder

Parameters

interface AddLiquidityParams {
  owner: PublicKey;              // The owner of the position
  pool: PublicKey;               // The pool address
  position: PublicKey;           // The position address
  positionNftMint: PublicKey;    // The position NFT mint
  liquidityDeltaQ64: BN;         // The amount of liquidity to add in Q64 format
  maxAmountTokenA: BN;           // Maximum amount of token A to use
  maxAmountTokenB: BN;           // Maximum amount of token B to use
  tokenAAmountThreshold: BN;     // Minimum acceptable token A amount (slippage protection)
  tokenBAmountThreshold: BN;     // Minimum acceptable token B amount (slippage protection)
  tokenAMint: PublicKey;         // The mint of token A
  tokenBMint: PublicKey;         // The mint of token B
  tokenAVault: PublicKey;        // The pool's token A vault
  tokenBVault: PublicKey;        // The pool's token B vault
  tokenAProgram: PublicKey;      // Token program for token A
  tokenBProgram: PublicKey;      // Token program for token B
}

Returns

A transaction builder (TxBuilder) that can be used to build, sign, and send the transaction.

Example

// Calculate liquidity delta first
const liquidityDelta = await cpAmm.getLiquidityDelta({
  maxAmountTokenA: new BN(1_000_000_000), // 1,000 USDC
  maxAmountTokenB: new BN(5_000_000_000), // 5 SOL
  sqrtPrice: poolState.sqrtPrice,
  sqrtMinPrice: MIN_SQRT_PRICE,
  sqrtMaxPrice: MAX_SQRT_PRICE
});

// Add liquidity
const addLiquidityTx = await cpAmm.addLiquidity({
  owner: wallet.publicKey,
  pool: poolAddress,
  position: positionAddress,
  positionNftMint: positionNftMint,
  liquidityDeltaQ64: liquidityDelta,
  maxAmountTokenA: new BN(1_000_000_000),
  maxAmountTokenB: new BN(5_000_000_000),
  tokenAAmountThreshold: new BN(0),
  tokenBAmountThreshold: new BN(0),
  tokenAMint: poolState.tokenAMint,
  tokenBMint: poolState.tokenBMint,
  tokenAVault: poolState.tokenAVault,
  tokenBVault: poolState.tokenBVault,
  tokenAProgram: TOKEN_PROGRAM_ID,
  tokenBProgram: TOKEN_PROGRAM_ID
});

const tx = await addLiquidityTx.transaction();
const signature = await wallet.sendTransaction(tx, connection);

Notes

  • Calculate the liquidity delta first using getLiquidityDelta
  • The SDK handles wrapping/unwrapping of SOL automatically
  • Token accounts are created automatically if they don’t exist
  • Set appropriate thresholds to protect against slippage