Simulation & Benchmark environment for Solana's Proprietary AMMs. The setup relies on Litesvm for local, consistent and expedited execution. Additionally, since some proprietary AMMs block swaps originating from direct offchain calls, we rely on a custom router program - magnus-router - to facilitate the swap execution.
Supported Prop AMMs:
- HumidiFi
- SolFiV2
- ObricV2
- ZeroFi
- TesseraV
- GoonFi
- BisonFi
The swaps can be done either with the local static accounts/programs that can be found at ./cfg/ or with the current live ones by fetching them on-the-go. By default all swaps & benchmark simulations are done with live accounts/programs.
The markets for each Prop AMM are specified in setup.toml.
Possible modes of execution include:
- single - Run a single swap route across one or more Prop AMMs with specified weights. The route can go through an arbitrary combination of Prop AMMs.
- multi - Execute swaps across nested Prop AMM routes. Each inner list represents a single route, each route possibly going through multiple Prop AMMs.
- benchmark - Benchmark swaps for any of the implemented Prop AMMs by specifying, optionally, the accounts, src/dst tokens and range size. Benchmark data can be visualised with plot.py.
- fetch-accounts - Fetch accounts for specified PMMs via RPC and save them locally (presumably for later usage).
- fetch-programs - Fetch programs for specified PMMs via RPC and save them locally (presumably for later usage).
Exchange rate & CU plots for benchmarked swaps at slot 397549538:
Figure 1: Exchange rate for benchmarked swaps
Some Prop AMMs tend to provide different — preferential — rates for whitelisted set of addresses. Depending on who's the source of CPI, you might get different quotes - You can optionally simulate swaps / benchmarks spoofed as one of: Jupiter, OkxLabs, DFlow or Titan.
Figure 3: SolFiV2's rates depending on the source of CPI
Figure 4: Tessera's rates depending on the source of CPI
Build the project
cargo build --release
./target/release/pmm-sim single --amount-in=15000 --pmms=humidifi --weights=100 \
--src-token=USDC --dst-token=WSOL
./target/release/pmm-sim single --amount-in=69000 --pmms=humidifi,bisonfi --weights=25,75 \
--src-token=USDC --dst-token=WSOL
Swap 375 WSOL for USDC using Tessera and SolFiV2, in one route, split evenly - 187,5 WSOL per Prop AMM, spoofed as DFlow.
./target/release/pmm-sim single --spoof=dflow --amount-in=375 --pmms=tessera,solfi-v2 \
--weights=50,50 --src-token=WSOL --dst-token=USDC
Swap 100 WSOL for USDC using SolFiV2, HumidiFi, and Tessera, in one route, split 33,33,34 WSOL per Prop AMM.
./target/release/pmm-sim single --amount-in=100 --pmms=solfi-v2,humidifi,tessera \
--weights=33,33,34 \
--src-token=WSOL --dst-token=USDC \
--jit-accounts=false --jit-programs=false
./target/release/pmm-sim single --amount-in=10000 --pmms=obric-v2 --weights=100 \
--src-token=USDC --dst-token=USDT
Swap 103 WSOL for USDC in a multi-route swap, 100 WSOL via HumidiFi and SolFiV2 (split 92%/8%) in one route, and 3 WSOL via Tessera in another route.
./target/release/pmm-sim multi --amount-in=100,3 --pmms="[[humidifi,solfi-v2],[tessera]]" \
--weights="[[92,8],[100]]"
Execute two routes, the first swapping 150,000 USDC for WSOL using HumidiFi and SolFiV2 (split 25%/75%), the second swapping 1000 USDC for WSOL using GoonFi.
RUST_LOG=debug ./target/release/pmm-sim multi --amount-in=150000,1000 \
--pmms="[[humidifi,solfi-v2],[goonfi]]" --weights="[[25,75],[100]]" \
--src-token=USDC --dst-token=WSOL --jit-accounts=true
Benchmark swaps on HumidiFi,Tessera,SolFiV2 and GoonFi, from 1 to 4000 WSOL to USDC, in increments of 1 WSOL. The results are saved at ./datasets.
./target/release/pmm-sim benchmark --pmms=humidifi,tessera,solfi-v2,goonfi \
--range=1.0,4000.0,1.0 --src-token=wsol --dst-token=usdc
Benchmark swaps on Tessera and SolFiV2, from 1 to 250 WSOL, in increments of 0.01 WSOL. The results are saved at ./datasets.
./target/release/pmm-sim benchmark --pmms=tessera,solfi-v2 \
--range=1.0,250.0,0.01 --src-token=wsol --dst-token=usdc
Benchmark swaps (USDC->WSOL) on HumidiFi and SolFiV2, from 10K to 100K USDC, in increments of 100 USDC. The results are saved at ./datasets.
./target/release/pmm-sim benchmark --pmms=humidifi,solfi-v2 \
--range=10000,100000,100 --src-token=usdc --dst-token=wsol
Generated benchmark data can be plotted through ./scripts/plot.py, like so:
./scripts/plot.py ./datasets/389141713*
./target/release/pmm-sim fetch-accounts
./target/release/pmm-sim fetch-accounts --pmms=humidifi,solfi-v2
./target/release/pmm-sim fetch-programs
./target/release/pmm-sim fetch-programs --pmms=bisonfi,tessera
All datasets are saved as parquet and available at datasets. To peek at the data through cli:
duckdb -csv \
-c "SELECT * FROM 'datasets/389129965_goonfi_4uWuh9fC7rrZKrN8ZdJf69MN1e2S7FPpMqcsyY1aof6K_20251225-212154.parquet'" \
| column -t -s ,Accounts are by default loaded (saved) from (at) cfg/accounts. Tweaking the source/destination is possible via --accounts-path or ACCOUNTS_PATH env variable.
Programs are by default loaded (saved) from (at) cfg/programs. Tweaking the source/destination is possible via --programs-path or PROGRAMS_PATH env variable.
Datasets are by default loaded (saved) from (at) datasets. Tweaking the source/destination is possible via --datasets-path or DATASETS_PATH env variable.
The supported tokens are loaded from cfg/tokens.json. Tweaking the source is possible via --tokens-path or TOKENS_PATH env variable.
Check out the CLI subcommands for additional clues (i.e pmm-sim single --help)
$ pmm-sim --help
Simulation environment for Solana's Proprietary AMMs.
Simulate swaps and Benchmark performance across *any* of the major Solana Prop AMMs.
Usage: pmm-sim <COMMAND>
Commands:
single Run a single swap route across one or more Prop AMMs with specified weights.
multi Execute multiple swap routes across nested Prop AMM routes. Each inner list represents a single route, each route possibly going through multiple Prop AMMs.
benchmark Benchmark swaps for any one of the implemented Prop AMMs by specifying, optionally, the accounts, src/dst tokens and step size
fetch-accounts Fetch accounts from the specified Pmms via RPC and save them locally (presumably for later usage).
fetch-programs Fetch programs from the specified Pmms via RPC and save them locally (presumably for later usage).
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
License: MIT
