Skip to content

Repository files navigation

token-swap-program

An Anchor program for Solana that runs a single-sided SOL-to-SPL-token swap pool at a fixed price. A pool authority creates a pool for one token mint, liquidity is deposited into the pool's token vault, and users swap SOL for that token at the price set at pool creation. Withdrawals from the pool require both the pool authority and a separate master authority to sign.

Architecture

The program (programs/swap) is built around one PDA-owned config account and two PDA-owned vaults, all derived from the same authority and token_mint_address:

Account Seeds Purpose
pool_config_account ["pool_config_account_seed", authority, token_mint_address] Stores token_price, is_active, the mint, the two vault addresses, authority, and master_authority.
pool_token_account ["pool_token_account_seed", authority, token_mint_address, pool_config_account] SPL token account, owned by the pool_config_account PDA. Holds the token side of the pool's liquidity.
pool_native_account ["pool_native_account_seed", authority, token_mint_address, pool_config_account] Plain system account, owned by the pool_config_account PDA. Holds the SOL collected from swaps.

Diagram of the swap pool: a Config Account storing token price, master authority and bump seeds, linked to a Token Account and a Native Account. A user transfers SOL to the Native Account and receives tokens from the Token Account. The pool authority calls Init pool and Add Liquid, and the Master Authority holds withdraw permission over both vaults.

Instructions (programs/swap/src/instructions/):

  • init_instruction (init_pool.rs) — creates pool_config_account, pool_token_account, and pool_native_account, and records authority and master_authority on the config account. Takes token_price: u64, the number of base token units paid out per LAMPORTS_PER_SOL (1 SOL) of input — the program does raw integer math and does not adjust for the mint's decimals. Only authority signs; master_authority is recorded but does not need to sign pool creation.
  • add_liquid_instruction (add_liquid.rs) — transfers amount of the pool's token from depositor_token_account into pool_token_account. Any signer holding the token can call this. There is no LP receipt or share accounting: a deposit cannot be withdrawn by the depositor, only by master_authority.
  • swap_token (swap_token.rs) — a user sends lamport_amount of SOL into pool_native_account and receives token_price * lamport_amount / LAMPORTS_PER_SOL of the token from pool_token_account, into an associated token account created for them if needed. Fails if pool_config_account.is_active is false.
  • update_config_instruction (update_pool_config.rs) — sets pool_config_account.is_active to the given disable: bool argument. Only authority can call it.
  • withdraw_native_instruction / drain_native_instruction (withdraw_native_pool.rs) — move a given amount, or the full balance, of SOL out of pool_native_account to master_authority. Require both authority and master_authority to sign.
  • withdraw_token_instruction / drain_token_instruction (withdraw_token_pool.rs) — move a given amount, or the full balance, of the pool token out of pool_token_account to an associated token account for master_authority. Require both authority and master_authority to sign.

In short: liquidity deposits are permissionless, but only the pool's authority and master_authority together can move funds back out.

Prerequisites

The versions below are the ones this program is verified to build with. rust-toolchain.toml pins the Rust channel and Anchor.toml pins the Anchor version, so a fresh clone should resolve most of this automatically.

  • Rust 1.96.0, pinned by rust-toolchain.toml and installed by rustup
  • Solana CLI 2.2.0 (Agave) — install guide
  • Anchor CLI 0.32.1, installed and pinned with avm: avm install 0.32.1 && avm use 0.32.1
  • Node.js 20.x

The Anchor CLI version must match the anchor-lang version in programs/swap/Cargo.toml. A mismatch produces an IDL the TypeScript client cannot read.

Setup and build

git clone https://github.com/defi0x1/token-swap-program.git
cd token-swap-program
npm install

Anchor needs a wallet keypair at the path set in Anchor.toml's [provider] wallet. If you don't already have one at that path, generate it:

solana-keygen new -o ~/.config/solana/id.json

Build the program and generate its IDL:

anchor build

The program keypair lives in target/, which is not committed, so the first build on a new machine generates a fresh program ID that will not match the one in Anchor.toml and declare_id!. Bring all three back into agreement:

anchor keys sync
anchor build

This is normal Anchor behaviour, not a defect. Only a deployed program needs a stable ID, and that keypair should be kept outside the repository.

Running the tests

Start a local validator in one terminal:

solana-test-validator --reset

In another terminal, run the test suite against it:

anchor test --skip-local-validator

This builds the program, deploys it to the running local validator, and runs tests/swap.ts, which airdrops SOL to fresh keypairs, creates a token mint, initializes a pool, and exercises add-liquidity, swap, withdraw, and drain, including their failure cases.

Terminal output of the Anchor test suite: an initialization test, add-liquidity and swap tests each with a passing case and an insufficient-funds failure case, and withdraw and drain tests for both the token and native vaults, ending with "11 passing".

All accounts used in the tests — the pool authority, the user, and the master authority — are keypairs generated at runtime with anchor.web3.Keypair.generate() and funded with solana-test-validator airdrops. No private key is committed to this repository.

Security

This is example and educational code for building an Anchor-based swap program. It has not been audited. Do not deploy it to Solana mainnet with real funds.

See SECURITY.md for how to report a vulnerability.

License

MIT

About

Example token program

Topics

Resources

Contributing

Security policy

Stars

18 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages