A first deposit mints LP and locks minimum liquidity
Source: crates/amm-frood-test/tests/add_liquidity.rs L101
- Given: admin initializes pool with seed=0, fee_bps=30
- When: pool is initialized
- Given: Alice has 10,000 X and 40,000 Y
- When: Alice deposits 1000 X and 4000 Y, requesting at least 1000 LP
Result: ✓ success — 80926 CU
sequenceDiagram
participant p0 as Alice
participant p1 as Amm
participant p2 as splAssociatedTokenAccount
participant p3 as token
participant p4 as system
p0->>p1: AddLiquidity
activate p1
p1->>p2: create
activate p2
p2->>p3: getAccountDataSize
activate p3
p3-->>p2: ✓ 183cu
deactivate p3
p2->>p4: createAccount
activate p4
p4-->>p2: ✓
deactivate p4
p2->>p3: initializeImmutableOwner
activate p3
p3-->>p2: ✓ 38cu
deactivate p3
p2->>p3: initializeAccount3
activate p3
p3-->>p2: ✓ 235cu
deactivate p3
p2-->>p1: ✓ 20916cu
deactivate p2
p1->>p3: transferChecked
activate p3
p3-->>p1: ✓ 105cu
deactivate p3
p1->>p3: transferChecked
activate p3
p3-->>p1: ✓ 105cu
deactivate p3
p1->>p3: mintTo
activate p3
p3-->>p1: ✓ 119cu
deactivate p3
p1->>p3: mintTo
activate p3
p3-->>p1: ✓ 119cu
deactivate p3
p1-->>p0: ✓ 80926cu
deactivate p1
Authority
flowchart LR
Amm["Amm"]:::program
Alice(["Alice"]):::signer
mintLpconfig0[("mintLp(config(0))")]:::state
vaultXconfig0Mint[("vaultX(config(0), Mint)")]:::state
vaultYconfig0Mint2[("vaultY(config(0), Mint 2)")]:::state
lpVaultconfig0mintLpconfig0[("lpVault(config(0), mintLp(config(0)))")]:::state
userXAliceMint[("userX(Alice, Mint)")]:::state
userYAliceMint2[("userY(Alice, Mint 2)")]:::state
userLpAlicemintLpconfig0(["userLp(Alice, mintLp(config(0)))"]):::signer
splAssociatedTokenAccount["splAssociatedTokenAccount"]:::program
token["token"]:::program
system["system"]:::program
config0(["config(0)"]):::signer
Alice -->|signs| Amm
Amm -->|writes| mintLpconfig0
Amm -->|writes| vaultXconfig0Mint
Amm -->|writes| vaultYconfig0Mint2
Amm -->|writes| lpVaultconfig0mintLpconfig0
Amm -->|writes| userXAliceMint
Amm -->|writes| userYAliceMint2
Amm -->|writes| userLpAlicemintLpconfig0
Alice -->|signs| splAssociatedTokenAccount
splAssociatedTokenAccount -->|writes| userLpAlicemintLpconfig0
Alice -->|signs| system
userLpAlicemintLpconfig0 -->|signs| system
token -->|writes| userLpAlicemintLpconfig0
token -->|writes| userXAliceMint
token -->|writes| vaultXconfig0Mint
Alice -->|signs| token
token -->|writes| userYAliceMint2
token -->|writes| vaultYconfig0Mint2
token -->|writes| mintLpconfig0
config0 -->|signs| token
token -->|writes| lpVaultconfig0mintLpconfig0
classDef program fill:#dae8fc,stroke:#6c8ebf;
classDef signer fill:#d5e8d4,stroke:#82b366;
classDef state fill:#ffe6cc,stroke:#d79b00;
Ownership
flowchart LR
system["system"]:::program
Alice[("Alice")]:::state
token["token"]:::program
mintLpconfig0[("mintLp(config(0))")]:::state
vaultXconfig0Mint[("vaultX(config(0), Mint)")]:::state
vaultYconfig0Mint2[("vaultY(config(0), Mint 2)")]:::state
lpVaultconfig0mintLpconfig0[("lpVault(config(0), mintLp(config(0)))")]:::state
userXAliceMint[("userX(Alice, Mint)")]:::state
userYAliceMint2[("userY(Alice, Mint 2)")]:::state
userLpAlicemintLpconfig0[("userLp(Alice, mintLp(config(0)))")]:::state
system -->|owns| Alice
token -->|owns| mintLpconfig0
token -->|owns| vaultXconfig0Mint
token -->|owns| vaultYconfig0Mint2
token -->|owns| lpVaultconfig0mintLpconfig0
token -->|owns| userXAliceMint
token -->|owns| userYAliceMint2
token -->|owns| userLpAlicemintLpconfig0
classDef program fill:#dae8fc,stroke:#6c8ebf;
classDef signer fill:#d5e8d4,stroke:#82b366;
classDef state fill:#ffe6cc,stroke:#d79b00;
Accounts
| Account | Signer | Writable | Owner |
|---|---|---|---|
| Alice | ✓ | ✓ | system |
| Mint | · | · | token |
| Mint 2 | · | · | token |
| config(0) | · | · | Amm |
| mintLp(config(0)) | · | ✓ | token |
| vaultX(config(0), Mint) | · | ✓ | token |
| vaultY(config(0), Mint 2) | · | ✓ | token |
| lpVault(config(0), mintLp(config(0))) | · | ✓ | token |
| userX(Alice, Mint) | · | ✓ | token |
| userY(Alice, Mint 2) | · | ✓ | token |
| userLp(Alice, mintLp(config(0))) | · | ✓ | token |
| token | · | · | BPFL… |
| splAssociatedTokenAccount | · | · | BPFL… |
| system | · | · | Nati… |
Call tree and logs
A first deposit mints LP and locks minimum liquidity
Alice (80926cu)
└─ Amm::AddLiquidity ✓ 80926cu
├─ splAssociatedTokenAccount::create ✓ 20916cu
│ ├─ token::getAccountDataSize ✓ 183cu
│ ├─ system::createAccount ✓
│ ├─ token::initializeImmutableOwner ✓ 38cu
│ └─ token::initializeAccount3 ✓ 235cu
├─ token::transferChecked ✓ 105cu
├─ token::transferChecked ✓ 105cu
├─ token::mintTo ✓ 119cu
└─ token::mintTo ✓ 119cu
Program 5aDxxnPDGeVEuLXerisV4GF5f8tcwTjbMK7Bn1dYUXSi invoke [1]
Program log: Instruction: AddLiquidity
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [2]
Program log: Create
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 183 of 168396 compute units
Program return: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA pQAAAAAAAAA=
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program 11111111111111111111111111111111 invoke [3]
Program 11111111111111111111111111111111 success
Program log: Initialize the associated token account
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 38 of 163303 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 235 of 160841 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 20916 of 181239 compute units
Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 105 of 125678 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 105 of 123670 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 119 of 121794 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 119 of 119938 compute units
Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success
Program 5aDxxnPDGeVEuLXerisV4GF5f8tcwTjbMK7Bn1dYUXSi consumed 80926 of 200000 compute units
Program 5aDxxnPDGeVEuLXerisV4GF5f8tcwTjbMK7Bn1dYUXSi success
Integration
What an integrator writes: the derivations and the instruction, byte-identical to the transaction this page witnessed. Addresses are this scenario’s; supply your own.
#![allow(unused)]
fn main() {
use solana_instruction::{AccountMeta, Instruction};
use solana_pubkey::{pubkey, Pubkey};
// --- addLiquidity ---
let program_id: Pubkey = pubkey!("5aDxxnPDGeVEuLXerisV4GF5f8tcwTjbMK7Bn1dYUXSi");
let user: Pubkey = pubkey!("C9Q33D4bhNQN8SFaAQCZke6DFkecWygY1HSsiYXuDSTW"); // Alice: this scenario's value; supply your own
let mint_x: Pubkey = pubkey!("14s1WJtcVpTaZu3msFwBCqiXN4vXsnUmgPbYQmVihg9k"); // Mint: this scenario's value; supply your own
let mint_y: Pubkey = pubkey!("3u6JCVjEDMjoVNS91QDBDpzAsZzffy1e3vaJm6rg7baa"); // Mint 2: this scenario's value; supply your own
let config: Pubkey = pubkey!("DWm4MrK6YroDTYNXjcpjh7k5TLbriQDfkQjePWuAmLVc"); // config(0): this scenario's value; supply your own
let token_program: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); // the token program
let associated_token_program: Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); // the splAssociatedTokenAccount program
let system_program: Pubkey = pubkey!("11111111111111111111111111111111"); // the system program
let mint_lp = Pubkey::find_program_address(&[b"lp", config.as_ref()], &program_id).0;
let vault_x: Pubkey = pubkey!("Huqnh3ixPRjEVaMhA4vJKzsS2fL5DS8cPz2dQrKEbAtJ"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let vault_y: Pubkey = pubkey!("BYCoofMdGc5jQL9TzNbVaWHCp4n2TWBip1dkrw2LWHys"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let lp_vault: Pubkey = pubkey!("2z79VArZvbErd7GmPasiLyTxnFJcbNZNEnWGVzojik7t"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let user_x: Pubkey = pubkey!("7gaYzTKg2xKMSsrxjFhRbmaH1Smjbn6Z75G2rBnPBR3c"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let user_y: Pubkey = pubkey!("79FymYMZPJgpnnW5exzdgfjXqYurPSdFHXu3Ppb3mVEX"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let user_lp: Pubkey = pubkey!("2t5PJLf5cyKVSd5WHe3e4DbPhd4zZXtsNVDQSRB8eUmc"); // derivation elided (a seed's encoding is beyond this snippet); address from the run
let ix = Instruction {
program_id,
accounts: vec![
AccountMeta::new(user, true), // user
AccountMeta::new_readonly(mint_x, false), // mintX
AccountMeta::new_readonly(mint_y, false), // mintY
AccountMeta::new_readonly(config, false), // config
AccountMeta::new(mint_lp, false), // mintLp
AccountMeta::new(vault_x, false), // vaultX
AccountMeta::new(vault_y, false), // vaultY
AccountMeta::new(lp_vault, false), // lpVault
AccountMeta::new(user_x, false), // userX
AccountMeta::new(user_y, false), // userY
AccountMeta::new(user_lp, false), // userLp
AccountMeta::new_readonly(token_program, false), // tokenProgram
AccountMeta::new_readonly(associated_token_program, false), // associatedTokenProgram
AccountMeta::new_readonly(system_program, false), // systemProgram
],
// discriminator ++ borsh(amountA: 1000, amountB: 4000, minLpTokens: 1000)
data: vec![0xb5, 0x9d, 0x59, 0x43, 0x8f, 0xb6, 0x34, 0x48, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
};
}
The same call through the Codama-generated TypeScript client (the async builder derives every PDA the resolver derived here, so only the free inputs appear):
import { address } from "@solana/kit";
import { getAddLiquidityInstructionAsync } from "your-generated-client";
const ix = await getAddLiquidityInstructionAsync({
user: userSigner, // Alice (a TransactionSigner)
mintX: address("14s1WJtcVpTaZu3msFwBCqiXN4vXsnUmgPbYQmVihg9k"), // Mint
mintY: address("3u6JCVjEDMjoVNS91QDBDpzAsZzffy1e3vaJm6rg7baa"), // Mint 2
config: address("DWm4MrK6YroDTYNXjcpjh7k5TLbriQDfkQjePWuAmLVc"), // config(0)
// tokenProgram defaults to the token program
// associatedTokenProgram defaults to the splAssociatedTokenAccount program
// systemProgram defaults to the system program
amountA: 1000n,
amountB: 4000n,
minLpTokens: 1000n,
});