A decentralized Automated Market Maker (AMM) built on Solana using the Anchor framework. Implements a constant-product curve (x * y = k) for trustless token swaps, liquidity provisioning, and LP token management β fully on-chain.
graph TD
Client["π₯οΈ Client / dApp<br/>TypeScript + Anchor SDK"]
Solana["β‘ Solana Localnet<br/>Program ID: Abckz5L...rLWN"]
Program["π¦ AMM Program<br/>lib.rs"]
Initialize["ποΈ initialize<br/>seed Β· fee Β· authority"]
Deposit["π° deposit<br/>add liquidity Β· mint LP"]
Withdraw["π§ withdraw<br/>remove liquidity Β· burn LP"]
Swap["π swap<br/>is_x Β· amount_in Β· min_out"]
State["ποΈ Config PDA<br/>seed Β· fee Β· locked<br/>mint_x Β· mint_y<br/>config_bump Β· lp_bump"]
Client -->|RPC calls| Solana
Solana --> Program
Program --> Initialize
Program --> Deposit
Program --> Withdraw
Program --> Swap
Initialize -->|creates| State
Deposit -->|reads| State
Withdraw -->|reads| State
Swap -->|reads| State
graph TD
Wallet["π€ Initializer Wallet<br/>Payer + Signer"]
Config["π Config PDA<br/>seeds: b'config' + seed<br/>βββββββββββββββββββββ<br/>seed Β· fee Β· locked<br/>mint_x Β· mint_y<br/>config_bump Β· lp_bump"]
VaultX["π¦ Vault X ATA<br/>holds Token X<br/>authority = Config"]
VaultY["π¦ Vault Y ATA<br/>holds Token Y<br/>authority = Config"]
MintLP["πͺ LP Mint PDA<br/>seeds: b'lp' + config.key<br/>authority = Config<br/>decimals = 6"]
Wallet -->|initialize| Config
Config -->|owns| VaultX
Config -->|owns| VaultY
Config -->|mint authority| MintLP
flowchart TD
A(["π€ deposit<br/>amount_lp Β· max_x Β· max_y"])
B{Pool locked?}
C["β PoolLocked"]
D{Pool empty?<br/>supply = 0<br/>vault_x = 0<br/>vault_y = 0}
E["x = max_x<br/>y = max_y"]
F["ConstantProduct::<br/>xy_deposit_amounts_from_l()<br/>xΒ·y proportional to k"]
G{x β€ max_x<br/>AND y β€ max_y?}
H["β SlippageExceeded"]
I["CPI: transfer<br/>user_x βββΊ vault_x"]
J["CPI: transfer<br/>user_y βββΊ vault_y"]
K["CPI: mint_to<br/>LP tokens βββΊ user_lp"]
L(["β
Done"])
A --> B
B -->|YES| C
B -->|NO| D
D -->|YES| E
D -->|NO| F
E --> G
F --> G
G -->|NO| H
G -->|YES| I
I --> J
J --> K
K --> L
flowchart TD
A(["π€ swap<br/>is_x Β· amount_in Β· min_out"])
B{Pool locked?}
C["β PoolLocked"]
D{LP supply = 0?}
E["β NoLiquidityInPool"]
F["ConstantProduct::init<br/>vault_x Β· vault_y Β· supply Β· fee"]
G["c.swap<br/>LiquidityPair::X or Y<br/>calculates res.deposit<br/>and res.withdraw"]
H["CPI: transfer<br/>user_in βββΊ vault_in<br/>amount = res.deposit"]
I["CPI: transfer w/ signer<br/>vault_out βββΊ user_out<br/>amount = res.withdraw"]
J(["β
Done"])
A --> B
B -->|YES| C
B -->|NO| D
D -->|YES| E
D -->|NO| F
F --> G
G --> H
H --> I
I --> J
flowchart TD
A(["π€ withdraw<br/>amount_lp Β· min_x Β· min_y"])
B{Pool locked?}
C["β PoolLocked"]
D["ConstantProduct::<br/>xy_withdraw_amounts_from_l()<br/>calculate proportional x, y"]
E{x β₯ min_x<br/>AND y β₯ min_y?}
F["β SlippageExceeded"]
G["CPI: burn<br/>LP tokens from user_lp"]
H["CPI: transfer w/ signer<br/>vault_x βββΊ user_x"]
I["CPI: transfer w/ signer<br/>vault_y βββΊ user_y"]
J(["β
Done"])
A --> B
B -->|YES| C
B -->|NO| D
D --> E
E -->|NO| F
E -->|YES| G
G --> H
H --> I
I --> J
sequenceDiagram
participant User
participant AMM as AMM Program
participant Config as Config PDA
participant SPL as SPL Token Program
Note over User,SPL: Deposit β user signs directly
User->>AMM: deposit(amount, max_x, max_y)
AMM->>SPL: transfer(user_x β vault_x, authority=user)
AMM->>SPL: transfer(user_y β vault_y, authority=user)
AMM->>SPL: mint_to(user_lp, authority=Config PDA)
Config-->>SPL: signs via signer_seeds ["config", seed, bump]
SPL-->>User: LP tokens received β
Note over User,SPL: Swap β vault out requires PDA signer
User->>AMM: swap(is_x, amount_in, min_out)
AMM->>SPL: transfer(user_in β vault_in, authority=user)
AMM->>SPL: transfer(vault_out β user_out, authority=Config PDA)
Config-->>SPL: signs via signer_seeds ["config", seed, bump]
SPL-->>User: output tokens received β
anchor-amm/
βββ Anchor.toml # Workspace config, cluster, wallet
βββ Cargo.toml # Workspace root
βββ package.json # Anchor SDK, Mocha, Chai
βββ tsconfig.json # TypeScript config for tests
βββ rust-toolchain.toml # Pinned Rust 1.89.0
β
βββ migrations/
β βββ deploy.ts # Anchor deploy script
β
βββ programs/amm/src/
β βββ lib.rs # Entrypoint β routes all 4 instructions
β βββ constants.rs # Program-wide constants
β βββ error.rs # Custom AmmError codes
β β
β βββ instructions/
β β βββ initialize.rs # Pool init β Config PDA + vaults + LP mint
β β βββ deposit.rs # Add liquidity, mint LP tokens
β β βββ withdraw.rs # Remove liquidity, burn LP tokens
β β βββ swap.rs # Token swap via constant product curve
β β
β βββ state/
β βββ config.rs # Config account definition
β
βββ tests/
βββ amm.ts # Integration tests (Mocha + Anchor)
| Account | Role |
|---|---|
initializer |
Payer + signer |
mint_x / mint_y |
The two tokens forming the pair |
config |
PDA β central pool state |
mint_lp |
PDA β LP token mint (authority = config) |
vault_x / vault_y |
ATAs holding pool reserves |
| Param | Description |
|---|---|
amount |
LP tokens to mint |
max_x |
Max Token X to deposit (slippage guard) |
max_y |
Max Token Y to deposit (slippage guard) |
| Param | Description |
|---|---|
amount |
LP tokens to burn |
min_x |
Min Token X expected back (slippage guard) |
min_y |
Min Token Y expected back (slippage guard) |
| Param | Description |
|---|---|
is_x |
true = sell Token X, false = sell Token Y |
amount_in |
Amount of input token to sell |
min_amount_out |
Minimum output expected (slippage guard) |
pub struct Config {
pub seed: u64, // Unique pool identifier
pub authority: Option<Pubkey>, // Optional admin (can lock pool)
pub mint_x: Pubkey, // Token X mint address
pub mint_y: Pubkey, // Token Y mint address
pub fee: u16, // Swap fee in basis points (30 = 0.3%)
pub locked: bool, // Emergency lock flag
pub config_bump: u8, // PDA bump for config
pub lp_bump: u8, // PDA bump for LP mint
}Constant Product Curve (x * y = k) β The invariant governing all swaps and liquidity ops. The constant-product-curve crate handles all math β deposit ratios, swap output amounts, and fees.
Config PDA β Central pool account derived from a seed. Multiple independent pools can coexist by using different seeds.
LP Tokens β Minted to liquidity providers proportional to their pool share. Burning LP tokens returns the underlying assets.
Slippage Protection β Every instruction takes a min/max bound. If the pool ratio moves beyond tolerance, the transaction reverts.
PDA Vault Authority β Both vaults are ATAs owned by Config PDA. The program signs outgoing transfers using CpiContext::new_with_signer with seeds ["config", seed, bump] β no private key needed.
- Rust β pinned to
1.89.0 - Solana CLI
- Anchor CLI
v0.32.1 - Yarn
git clone https://github.com/Subodhkd001/anchor-amm.git
cd anchor-amm
yarn install
anchor build
anchor test| Layer | Technology |
|---|---|
| Smart Contract | Rust + Anchor 0.32.1 |
| Curve Math | constant-product-curve |
| Token Standard | SPL Token (anchor-spl) |
| Tests | TypeScript + Mocha + Chai |
| Network | Solana Localnet |
ISC