Automated Market Maker (AMM)

The Automated Market Maker (AMM) is a core component of the PumpFundler protocol, responsible for managing token liquidity and determining prices for buy and sell operations. It implements the bonding curve mechanism that governs token price dynamics.

Structure

The AMM class has the following structure:

export class AMM {

  constructor(

    public virtualSolReserves: bigint,

    public virtualTokenReserves: bigint,

    public realSolReserves: bigint,

    public realTokenReserves: bigint,

    public initialVirtualTokenReserves: bigint,

  ) {}



  // ... methods ...

}

Let’s break down each property:

  • virtualSolReserves: The virtual SOL balance used for price calculations.
  • virtualTokenReserves: The virtual token balance used for price calculations.
  • realSolReserves: The actual SOL balance in the AMM.
  • realTokenReserves: The actual token balance in the AMM.
  • initialVirtualTokenReserves: The initial virtual token reserves, used for scaling calculations.

Key Methods

Static Factory Methods

static fromGlobalAccount(global: GlobalAccount): AMM {

  return new AMM(

    global.initialVirtualSolReserves,

    global.initialVirtualTokenReserves,

    0n,

    global.initialRealTokenReserves,

    global.initialVirtualTokenReserves,

  );

}



static fromBondingCurveAccount(

  bonding_curve: BondingCurveAccount,

  initialVirtualTokenReserves: bigint,

): AMM {

  return new AMM(

    bonding_curve.virtualSolReserves,

    bonding_curve.virtualTokenReserves,

    bonding_curve.realSolReserves,

    bonding_curve.realTokenReserves,

    initialVirtualTokenReserves,

  );

}

These methods create an AMM instance from either a GlobalAccount or a BondingCurveAccount, initializing it with the appropriate reserve values.

getBuyPrice

getBuyPrice(tokens: bigint): bigint {

  const product_of_reserves =

    this.virtualSolReserves * this.virtualTokenReserves;

  const new_virtual_token_reserves = this.virtualTokenReserves - tokens;

  const new_virtual_sol_reserves =

    product_of_reserves / new_virtual_token_reserves + 1n;

  const amount_needed =

    new_virtual_sol_reserves > this.virtualSolReserves

      ? new_virtual_sol_reserves - this.virtualSolReserves

      : 0n;

  return amount_needed > 0n ? amount_needed : 0n;

}

This method calculates the price in SOL for buying a specific amount of tokens. It:

  1. Calculates the product of virtual reserves.
  2. Determines the new virtual token reserves after the purchase.
  3. Computes the new virtual SOL reserves.
  4. Returns the amount of SOL needed for the purchase.

applyBuy

applyBuy(token_amount: bigint): BuyResult {

  const final_token_amount =

    token_amount > this.realTokenReserves

      ? this.realTokenReserves

      : token_amount;

  const sol_amount = this.getBuyPrice(final_token_amount);



  this.virtualTokenReserves = this.virtualTokenReserves - final_token_amount;

  this.realTokenReserves = this.realTokenReserves - final_token_amount;



  this.virtualSolReserves = this.virtualSolReserves + sol_amount;

  this.realSolReserves = this.realSolReserves + sol_amount;



  return {

    token_amount: final_token_amount,

    sol_amount: sol_amount,

  };

}

This method executes a buy operation, updating the AMM’s state. It:

  1. Determines the final token amount (limited by available reserves).
  2. Calculates the SOL amount required.
  3. Updates virtual and real reserves.
  4. Returns a BuyResult with the token and SOL amounts involved.

getSellPrice

getSellPrice(tokens: bigint): bigint {

  const scaling_factor = this.initialVirtualTokenReserves;

  const token_sell_proportion =

    (tokens * scaling_factor) / this.virtualTokenReserves;

  const sol_received =

    (this.virtualSolReserves * token_sell_proportion) / scaling_factor;

  return sol_received < this.realSolReserves

    ? sol_received

    : this.realSolReserves;

}

This method calculates the amount of SOL to be received for selling a specific amount of tokens. It:

  1. Calculates the scaling factor.
  2. Determines the proportion of tokens being sold.
  3. Computes the SOL to be received.
  4. Returns the minimum of the calculated SOL and the real SOL reserves.

applySell

applySell(token_amount: bigint): SellResult {

  this.virtualTokenReserves = this.virtualTokenReserves + token_amount;

  this.realTokenReserves = this.realTokenReserves + token_amount;



  const sell_price = this.getSellPrice(token_amount);



  this.virtualSolReserves = this.virtualSolReserves - sell_price;

  this.realSolReserves = this.realSolReserves - sell_price;



  return {

    token_amount: token_amount,

    sol_amount: sell_price,

  };

}

This method executes a sell operation, updating the AMM’s state. It:

  1. Updates virtual and real token reserves.
  2. Calculates the sell price.
  3. Updates virtual and real SOL reserves.
  4. Returns a SellResult with the token and SOL amounts involved.

Usage in the SDK

The AMM class is primarily used internally by the PumpFundler SDK to:

  1. Calculate buy and sell prices for tokens.
  2. Execute buy and sell operations.
  3. Manage the state of the bonding curve for each token.

Here’s an example of how you might use the AMM class to simulate trading operations:

import { AMM, GlobalAccount, BondingCurveAccount } from "pumpfundler-sdk";



// Assume we have fetched globalAccount and bondingCurveAccount



// Create AMM instance from a bonding curve account

const amm = AMM.fromBondingCurveAccount(

  bondingCurveAccount,

  globalAccount.initialVirtualTokenReserves

);



// Simulate a buy operation

const buyAmount = 1000000n; // 1 token with 6 decimals

const buyPrice = amm.getBuyPrice(buyAmount);

console.log(`Price to buy \${buyAmount} tokens: \${buyPrice} SOL`);



const buyResult = amm.applyBuy(buyAmount);

console.log(`Buy executed: \${buyResult.token_amount} tokens for \${buyResult.sol_amount} SOL`);



// Simulate a sell operation

const sellAmount = 500000n; // 0.5 tokens with 6 decimals

const sellPrice = amm.getSellPrice(sellAmount);

console.log(`Price to sell \${sellAmount} tokens: \${sellPrice} SOL`);



const sellResult = amm.applySell(sellAmount);

console.log(`Sell executed: \${sellResult.token_amount} tokens for \${sellResult.sol_amount} SOL`);

AMM Dynamics

Understanding the AMM dynamics is crucial for participants in the PumpFundler ecosystem:

  1. Constant Product Formula: The AMM uses a constant product formula (virtualSolReserves * virtualTokenReserves = constant) to determine prices, ensuring that the product of reserves remains constant after trades.

  2. Price Slippage: Larger trade sizes will experience more slippage, as they have a more significant impact on the reserve ratios.

  3. Virtual vs. Real Reserves: The use of virtual reserves allows for more flexible pricing models, while real reserves represent the actual assets in the pool.

  4. Liquidity Depth: The difference between virtual and real reserves influences the liquidity depth and price stability of the token.