DAMM v1 SDK supports 4 types of pools:

  • Constant Product Pool (Volatile Pool) - For non-stablecoin pairs (e.g. SOL-USDC)
  • Stable Pool - For stablecoin pairs (e.g. USDC-USDT)
  • Stake2Earn Pool - DAMM v1 pool with Stake2Earn vault mechanism for top stakers

Below are code examples showing how to interact with the AmmImpl instance to create these pools and perform key DAMM v1 actions.

If the DAMM v1 Pool is being launched with an Alpha Vault, SOL or USDC must be used as the quote token.

Create Constant Product Pool (Volatile Pool)

This script creates a Volatile Pool, which is a Dynamic AMM Pool where one or both tokens are non-stablecoins (e.g. SOL-USDC).

import AmmImpl, { PROGRAM_ID } from '@meteora-ag/dynamic-amm-sdk';
import { derivePoolAddressWithConfig } from '@meteora-ag/dynamic-amm-sdk/dist/cjs/src/amm/utils';
import { BN } from 'bn.js';

// Token A/B address of the pool.
const tokenAMint = new PublicKey('...');
const tokenBMint = new PublicKey('...');

// Configuration address for the pool. It will decide the fees of the pool.
const config = new PublicKey('...');

// Amount of token A and B to be deposited to the pool.
const tokenAAmount = new BN(100_000);
const tokenBAmount = new BN(500_000);

// Get pool address
const programId = new PublicKey(PROGRAM_ID);
const poolPubkey = derivePoolAddressWithConfig(tokenAMint, tokenBMint, config, programId);

// Create pool
const transactions = await AmmImpl.createPermissionlessConstantProductPoolWithConfig(
  provider.connection,
  mockWallet.publicKey, // payer
  tokenAMint,
  tokenBMint,
  tokenAAmount,
  tokenBAmount,
  config,
  // { // if you need to set the activation point earlier than the default derived from the config
  //   activationPoint: startTime !== 'now' ? new BN(Math.floor(new Date(startTime).getTime() / 1000)) : undefined,
  // },
); 

for (const transaction of transactions) {
  transaction.sign(mockWallet.payer);
  const txHash = await provider.connection.sendRawTransaction(transaction.serialize());
  await provider.connection.confirmTransaction(txHash, 'finalized');
  console.log('transaction %s', txHash);
}

Create Stable Pool

This script creates a Stable Pool, which is a Dynamic AMM Pool where both tokens are stablecoins (e.g. USDC-USDT).

Note: When using createPermissionlessPool to create a permissionless stable pool, it requires the pool to be seeded with a 1:1 token amount (e.g. 1 USDC, 1 USDT).

import AmmImpl, { derivePoolAddress } from '@meteora-ag/dynamic-amm-sdk';
import { BN } from 'bn.js';

// Token A/B address of the pool.
const tokenAMint = new PublicKey('...');
const tokenBMint = new PublicKey('...');

const tokenADecimal = 6;
const tokenBDecimal = 6;

const feeBps = new BN(1); // 0.01%

// Get pool address
const poolPubkey = derivePoolAddress(
  provider.connection,
  tokenAMint,
  tokenBMint,
  tokenADecimal,
  tokenBDecimal,
  true, // stable
  feeBps,
);

// Create pool
const transactions = await AmmImpl.createPermissionlessPool(
  provider.connection,
  mockWallet.publicKey, // payer
  tokenAMint,
  tokenBMint,
  tokenAAmount,
  tokenBAmount,
  true, // stable,
  feeBps,
);

for (const transaction of transactions) {
  transaction.sign(mockWallet.payer);
  const txHash = await provider.connection.sendRawTransaction(transaction.serialize());
  await provider.connection.confirmTransaction(txHash, 'finalized');
  console.log('transaction %s', txHash);
}

Create Memecoin Pool

This script creates a Memecoin Pool, which is a Dynamic AMM Pool with a fee scheduler and initial liquidity permanently locked at creation.

import AmmImpl, { PROGRAM_ID } from '@meteora-ag/dynamic-amm-sdk';
import { derivePoolAddressWithConfig } from '@meteora-ag/dynamic-amm-sdk/dist/cjs/src/amm/utils';
import { BN } from 'bn.js';
import { roundToNearestMinutes } from 'date-fns';

// Token A/B address of the pool.
const memecoinMint = new PublicKey('...');
const tokenBMint = new PublicKey('...');

const memecoinAmount = new BN(100_000);
const tokenBAmount = new BN(500_000);

// Create pool
const programId = new PublicKey(PROGRAM_ID);

const CONFIG_KEY = new PublicKey('..');
const feeConfigurations = await AmmImpl.getFeeConfigurations(provider.connection, {
  programId,
});
const feeConfig = feeConfigurations.find(({ publicKey }) => publicKey.equals(CONFIG_KEY));

const transactions = await AmmImpl.createPermissionlessConstantProductMemecoinPoolWithConfig(
  provider.connection,
  mockWallet.publicKey, // payer
  memecoinMint,
  tokenBMint,
  memecoinAmount,
  tokenBAmount,
  feeConfig.publicKey,
  { isMinted: true },
);

Create Stake2Earn Pool

This script creates a Stake2Earn Pool, which is a Memecoin Pool with a Stake2Earn Vault mechanism, where top stakers earn fees from the locked liquidity in the Stake2Earn Vault.

import AmmImpl, { PROGRAM_ID } from '@meteora-ag/dynamic-amm-sdk';
import { derivePoolAddressWithConfig } from '@meteora-ag/dynamic-amm-sdk/dist/cjs/src/amm/utils';
import { BN } from 'bn.js';
import { roundToNearestMinutes } from 'date-fns';

// Token A/B address of the pool.
const memecoinMint = new PublicKey('...');
const tokenBMint = new PublicKey('...');

const memecoinAmount = new BN(100_000);
const tokenBAmount = new BN(500_000);

// Create pool
const programId = new PublicKey(PROGRAM_ID);

const CONFIG_KEY = new PublicKey('..');
const feeConfigurations = await AmmImpl.getFeeConfigurations(provider.connection, {
  programId,
});
const feeConfig = feeConfigurations.find(({ publicKey }) => publicKey.equals(CONFIG_KEY));

// with Stake2Earn vault
const feeDurationInDays = 7;
const numOfStakers = 1000;
const feeClaimStartTime = roundToNearestMinutes(new Date(), {
  nearestTo: 30,
});
const cooldownDurationInHours = 6;

const transactions = await AmmImpl.createPermissionlessConstantProductMemecoinPoolWithConfig(
  provider.connection,
  mockWallet.publicKey, // payer
  memecoinMint,
  tokenBMint,
  memecoinAmount,
  tokenBAmount,
  feeConfig.publicKey,
  { isMinted: true },
  {
    feeVault: {
      secondsToFullUnlock: feeDurationInDays ? new BN(feeDurationInDays * 86400) : new BN(0),
      topListLength: numOfStakers || 0,
      startFeeDistributeTimestamp: feeClaimStartTime ? new BN(feeClaimStartTime.getTime() / 1000) : null,
      unstakeLockDuration: cooldownDurationInHours ? new BN(cooldownDurationInHours * 3600) : new BN(0),
    },
    // other options
  },
);

for (const transaction of transactions) {
  transaction.sign(mockWallet.payer);
  const txHash = await provider.connection.sendRawTransaction(transaction.serialize());
  await provider.connection.confirmTransaction(txHash, 'finalized');
  console.log('transaction %s', txHash);
}
For Alpha Vault integration, please refer to Alpha Vault Integration.

Common Pool Operations

These operations are common across all pool types.

Get the pool’s LP token supply

// To refetch the pool's latest supply
// Alternatively, use `AmmImpl.poolState.lpSupply`
const lpSupply = await pool.getLpSupply();

Check user’s LP balance in the pool

// Get the user's ATA LP balance
const userLpBalance = await pool.getUserBalance(mockWallet.publicKey);

Update pool state

// It's recommended to update the deposit before perform any quotation
await pool.updateState();

Constant Product Pool Operations

Operations specific to Constant Product (Volatile) pools.

Deposit

const balance = true;
const slippage = 0.1; // Max to 2 decimal place
const inAmountALamport = new BN(1 * 10 ** constantProductPool.tokenAMint.decimals);

// Get deposit quote
const { poolTokenAmountOut, tokenAInAmount, tokenBInAmount } = constantProductPool.getDepositQuote(
  inAmountALamport,
  new BN(0),
  balance,
  slippage,
);

const depositTx = await constantProductPool.deposit(
  mockWallet.publicKey,
  tokenAInAmount,
  tokenBInAmount,
  poolTokenAmountOut,
); // Web3 Transaction Object
const depositResult = await provider.sendAndConfirm(depositTx); // Transaction hash

Withdraw

const slippage = 0.1; // Max to 2 decimal place
const outTokenAmountLamport = new BN(0.1 * 10 ** constantProductPool.decimals);

const { poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount } = constantProductPool.getWithdrawQuote(
  outTokenAmountLamport,
  slippage,
); // use lp balance for full withdrawal
const withdrawTx = await constantProductPool.withdraw(
  mockWallet.publicKey,
  poolTokenAmountIn,
  tokenAOutAmount,
  tokenBOutAmount,
); // Web3 Transaction Object
const withdrawResult = await provider.sendAndConfirm(withdrawTx); // Transaction hash

Swap

const slippage = 0.1; // Max to 2 decimal place
const inAmountLamport = new BN(0.1 * 10 ** constantProductPool.tokenB.decimals);

const { minSwapOutAmount } = constantProductPool.getSwapQuote(
  new PublicKey(constantProductPool.tokenB.address),
  inAmountLamport,
  slippage,
);

const swapTx = await constantProductPool.swap(
  mockWallet.publicKey,
  new PublicKey(constantProductPool.tokenB.address),
  inAmountLamport,
  minSwapOutAmount,
);
const swapResult = await provider.sendAndConfirm(swapTx);

Stable Pool Operations

Operations specific to Stable pools.

Balance deposit to stable pool

const slippage = 0.1; // Max to 2 decimal place
const balance = true;
const inAmountALamport = new BN(0.1 * 10 ** stablePool.tokenA.decimals);

const { poolTokenAmountOut, tokenAInAmount, tokenBInAmount } = stablePool.getDepositQuote(
  inAmountALamport,
  new BN(0),
  balance,
  slippage,
);

const depositTx = await stablePool.deposit(mockWallet.publicKey, tokenAInAmount, tokenBInAmount, poolTokenAmountOut); // Web3 Transaction Object
const depositResult = await provider.sendAndConfirm(depositTx); // Transaction hash

Double-sided imbalance deposit to stable pool

const slippage = 0.1; // Max to 2 decimal place
const balance = false;
const inAmountALamport = new BN(0.1 * 10 ** stablePool.tokenA.decimals);
const inAmountBLamport = new BN(0.1 * 10 ** stablePool.tokenB.decimals);

const { poolTokenAmountOut, tokenAInAmount, tokenBInAmount } = stablePool.getDepositQuote(
  inAmountALamport,
  inAmountBLamport,
  balance,
  slippage,
); // Web3 Transaction Object
const depositTx = await stablePool.deposit(mockWallet.publicKey, tokenAInAmount, tokenBInAmount, poolTokenAmountOut);
const depositResult = await provider.sendAndConfirm(depositTx); // Transaction hash

Single-sided imbalance deposit to stable pool

const slippage = 0.1; // Max to 2 decimal place
const balance = false;
const inAmountALamport = new BN(0.1 * 10 ** stablePool.tokenA.decimals);

const { poolTokenAmountOut, tokenAInAmount, tokenBInAmount } = stablePool.getDepositQuote(
  inAmountALamport,
  new BN(0),
  balance,
  slippage,
); // Web3 Transaction Object
const depositTx = await stablePool.deposit(mockWallet.publicKey, tokenAInAmount, tokenBInAmount, poolTokenAmountOut);
const depositResult = await provider.sendAndConfirm(depositTx); // Transaction hash

Balance withdraw from stable pool

const slippage = 0.1; // Max to 2 decimal place
const outTokenAmountLamport = new BN(0.1 * 10 ** stablePool.decimals);

const { poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount } = stablePool.getWithdrawQuote(
  outTokenAmountLamport,
  slippage,
); // use lp balance for full withdrawal
const withdrawTx = await stablePool.withdraw(mockWallet.publicKey, poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount); // Web3 Transaction Object
const withdrawResult = await provider.sendAndConfirm(withdrawTx);

Imbalance withdraw from stable pool

const slippage = 0.1; // Max to 2 decimal place
const outTokenAmountLamport = new BN(0.1 * 10 ** stablePool.decimals);

const { poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount } = stablePool.getWithdrawQuote(
  outTokenAmountLamport,
  slippage,
  new PublicKey(stablePool.tokenA.address), // Pass in token A/B mint to perform imbalanced withdraw
);
const withdrawTx = await stablePool.withdraw(mockWallet.publicKey, poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount); // Web3 Transaction Object
const withdrawResult = await provider.sendAndConfirm(withdrawTx);

Swap

const slippage = 0.1; // Max to 2 decimal place
const inAmountLamport = new BN(0.1 * 10 ** stablePool.tokenB.decimals);

const { minSwapOutAmount } = stablePool.getSwapQuote(
  new PublicKey(stablePool.tokenB.address),
  inAmountLamport,
  slippage,
);

const swapTx = await stablePool.swap(
  mockWallet.publicKey,
  new PublicKey(stablePool.tokenB.address),
  inAmountLamport,
  minSwapOutAmount,
);
const swapResult = await provider.sendAndConfirm(swapTx);

Memecoin Pool Operations

Operations specific to Memecoin pools.

Deposit

const balance = true;
const slippage = 0.1; // Max to 2 decimal place
const inAmountALamport = new BN(1 * 10 ** memecoinPool.tokenA.decimals);

// Get deposit quote for constant product
const { poolTokenAmountOut, tokenAInAmount, tokenBInAmount } = memecoinPool.getDepositQuote(
  inAmountALamport,
  new BN(0),
  balance,
  slippage,
);

const depositTx = await memecoinPool.deposit(mockWallet.publicKey, tokenAInAmount, tokenBInAmount, poolTokenAmountOut); // Web3 Transaction Object
const depositResult = await provider.sendAndConfirm(depositTx); // Transaction hash

Withdraw

const slippage = 0.1; // Max to 2 decimal place
const outTokenAmountLamport = new BN(0.1 * 10 ** memecoinPool.decimals);

const { poolTokenAmountIn, tokenAOutAmount, tokenBOutAmount } = memecoinPool.getWithdrawQuote(
  outTokenAmountLamport,
  slippage,
); // use lp balance for full withdrawal
const withdrawTx = await memecoinPool.withdraw(
  mockWallet.publicKey,
  poolTokenAmountIn,
  tokenAOutAmount,
  tokenBOutAmount,
); // Web3 Transaction Object
const withdrawResult = await provider.sendAndConfirm(withdrawTx); // Transaction hash

Swap

const slippage = 0.1; // Max to 2 decimal place
const inAmountLamport = new BN(0.1 * 10 ** memecoinPool.tokenB.decimals);

const { minSwapOutAmount } = memecoinPool.getSwapQuote(
  new PublicKey(memecoinPool.tokenB.address),
  inAmountLamport,
  slippage,
);

const swapTx = await memecoinPool.swap(
  mockWallet.publicKey,
  new PublicKey(memecoinPool.tokenB.address),
  inAmountLamport,
  minSwapOutAmount,
);
const swapResult = await provider.sendAndConfirm(swapTx);

Integration Considerations

Any constraints on the quote token?

No constraint on the quote token for a Dynamic AMM Pool. However, if the Dynamic AMM Pool is being launched with an Alpha Vault, SOL or USDC must be used as the quote token.

On DAMM v1, can you create two pools with the same token pair and configuration?

Using TypeScript SDK

Referencing index.ts in the SDK, there are various endpoints available. For example:

  • CreatePermissionlessConstantProductPoolWithConfig2: Creates a pool using a unique config; This endpoint allows the shortening of pool activation time.
  • CreatePermissionlessConstantProductPoolWithConfig: Creates a pool using a unique config; This endpoint does not allow the shortening of pool activation time.
  • CreatePermissionlessConstantProductMemecoinPoolwithConfig: Creates a DAMM v1 Memecoin-type Pool with a fee scheduler and liquidity locked at creation.
  • CreateCustomizablePermissionlessConstantProductPool: Creates a pool with customization, including fixed fee tier. Useful for token launches and launchpads.
  • CreatePermissionlessPool: Creates a pool using a set of default fixed fee tiers. This is mainly used to create a stable pool.

Technically, when using the TypeScript SDK, one pool can be created using one endpoint, and another pool (with the same fee configuration) can be created with a different endpoint.

But when using the same endpoint, only one pool with the specific fee configuration can be created. In other words, when using the same endpoint, it is not possible to create two pools with the same token pair and fee configuration.

If two pools with the same token pair and fee configuration are created (using different endpoints), they should both still be displayed on the Meteora website UI.