Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ secrecy = "0.10.3"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.148"
solana-client = "=3.0.0"
solana-commitment-config = "3.1.0"
solana-compute-budget = "=3.0.0"
solana-sdk = "3.0.0"
spl-associated-token-account = "8.0.0"
Expand Down
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pmm-sim

Simulation & Benchmark environment for Solana's Proprietary AMMs. The setup relies on [Litesvm](https://crates.io/crates/litesvm) for local, consistent and expedited execution. Additionally, since some proprietary AMMs block swaps originating from direct offchain calls, we rely on a custom onchain router - [Magnus](https://github.com/limechain/magnus) - to facilitate the swap execution.
Simulation & Benchmark environment for Solana's Proprietary AMMs. The setup relies on [Litesvm](https://crates.io/crates/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](https://github.com/limechain/magnus) - to facilitate the swap execution.

Supported Prop AMMs:

Expand All @@ -11,14 +11,14 @@ Supported Prop AMMs:
- [x] TesseraV
- [x] Goonfi

The swaps can be done either with the local static accounts that can be found at [cfg/accounts](./cfg/accounts) or with the current live accounts (by fetching them on-the-go). By default all swaps & benchmark simulations are done with live accounts.
The swaps can be done either with the local static accounts that can be found at [cfg/accounts](./cfg/accounts) or with the current live accounts (by fetching them on-the-go). By default all swaps & benchmark simulations are done with live accounts. The markets are specified in [setup.toml](./setup.toml).

Possible modes of execution include:

- **single** - Run a single swap route across one or more Prop AMMs with specified weights.
- **multi** - Execute swaps across nested Prop AMM routes. Each inner list represents a single route, each route possibly going through multiple Prop AMMs.
- **fetch-accounts** - Fetch accounts for specified PMMs via RPC and save them locally (presumably for later usage).
- **benchmark** - Benchmark swaps for any of the implemented Prop AMMs by specifying, optionally, the accounts, src/dst tokens and step size. Furthermore benchmark data can be visualised with [plot.py](./scripts/plot.py).
- **benchmark** - Benchmark swaps for any of the implemented Prop AMMs by specifying, optionally, the accounts, src/dst tokens and steps size. Benchmark data can be visualised with [plot.py](./scripts/plot.py).

Accounts are by default loaded (saved) from (at) [cfg/accounts](./cfg/accounts). Tweaking the source/destination is possible via `--accounts-path` or `ACCOUNTS_PATH` env variable.

Expand All @@ -38,16 +38,22 @@ _Figure 2: Compute unit usage_

### Single-route swaps

##### Swap 100 WSOL for USDC using Humidifi.
##### Swap 15K USDC for WSOL using Humidifi.

```
cargo r -- single --amount-in=100 --pmms=humidifi --weights=100
cargo r -- single --amount-in=15000 --pmms=humidifi --weights=100 --src-token=USDC --dst-token=WSOL
```

##### Swap 150,000 USDC for WSOL using Tessera and SolfiV2, in a route, split evenly - 75000 USDC per Prop AMM.
##### Swap 375 WSOL for USDC using Tessera and SolfiV2, in one route, split evenly - 187,5 WSOL per Prop AMM.

```
cargo r -- single --pmms=tessera,solfi-v2 --weights=50,50 --amount-in=150000 --src-token=USDC --dst-token=WSOL
cargo r -- single --pmms=tessera,solfi-v2 --weights=50,50 --amount-in=375 --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.

```
cargo r -- single --amount-in=100 --pmms=solfi-v2,humidifi,tessera --weights=33,33,34 --src-token=WSOL --dst-token=USDC --jit-accounts=false
```

##### Swaps 10,000 USDC for USDT using ObricV2.
Expand All @@ -61,7 +67,7 @@ cargo r -- single --amount-in=10000 --pmms=obric-v2 --weights=100 --src-token=US
##### 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 SolfiV2 in another route.

```
cargo r -- multi --pmms="[[humidifi,solfi-v2],[humidifi]]" --weights "[[92, 8],[100]]" --amount-in=100,3
cargo r -- multi --pmms="[[humidifi,solfi-v2],[humidifi]]" --weights="[[92, 8],[100]]" --amount-in=100,3
```

##### 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. Uses the static accounts (i.e the accounts found at [./cfg/accounts](./cfg/accounts)).
Expand All @@ -72,45 +78,52 @@ RUST_LOG=debug cargo r -- multi --pmms="[[humidifi,solfi-v2],[goonfi]]" --weight

### Benchmark swaps

##### Benchmark swaps on Humidifi, from 1 to 4000 WSOL to USDC, in increments of 1 WSOL, and save the results at [./datasets](./datasets).
##### Benchmark swaps on Humidifi,Tessera,SolfiV2 and Goonfi, from 1 to 4000 WSOL to USDC, in increments of 1 WSOL, and save the results at [./datasets](./datasets).

```
cargo r -- benchmark --step=1,4000,1 --pmms=humidifi --src-token=wsol --dst-token=usdc
cargo r -- benchmark --steps=1.0,4000.0,1.0 --pmms=humidifi,tessera,solfi-v2,goonfi --src-token=wsol --dst-token=usdc
```

##### Benchmark swaps (USDC->WSOL) on Humidifi, Tessera, SolfiV2 and Goonfi, from 10K to 100K USDC, in increments of 100 USDC, and save the results at [./datasets](./datasets).
##### Benchmark swaps (USDC->WSOL) on Humidifi and SolfiV2, from 10K to 100K USDC, in increments of 100 USDC, and save the results at [./datasets](./datasets).

```
cargo r -- benchmark --step=10000,100000,100 --pmms=humidifi,tessera,solfi-v2,goonfi --src-token=usdc --dst-token=wsol
cargo r -- benchmark --steps=10000,100000,100 --pmms=humidifi,solfi-v2 --src-token=usdc --dst-token=wsol
```

Once generated, the results can be plotted through [./scripts/plot.py](./scripts/plot.py), i.e:
Generated benchmark data can be plotted through [./scripts/plot.py](./scripts/plot.py), like so:

##### Plots all the local datasets for slot `389141713`.

```

./scripts/plot.py ./datasets/389141713*

```

### Fetch live accounts

##### Locally sync the current (live) accounts for all supported Prop AMMs.

```

cargo r -- fetch-accounts

```

##### Locally sync the current (live) accounts for Humidifi and SolfiV2.

```

cargo r -- fetch-accounts --pmms=humidifi,solfi-v2

```

---

Check out the CLI subcommands for additional clues (i.e `pmm-sim single --help`)

```

$ pmm-sim --help

Simulation environment for Solana's Proprietary AMMs.
Expand All @@ -128,4 +141,5 @@ Commands:
Options:
-h, --help Print help
-V, --version Print version

```
28 changes: 14 additions & 14 deletions setup.toml
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
# https://solscan.io/account/FksffEqnBRixYGR791Qw2MgdU7zNCpHVFYBL4Fa4qVuH
[humidifi]
market = "FksffEqnBRixYGR791Qw2MgdU7zNCpHVFYBL4Fa4qVuH"
base_token_acc = "C3FzbX9n1YD2dow2dCmEv5uNyyf22Gb3TLAEqGBhw5fY"
quote_token_acc = "3RWFAQBRkNGq7CMGcTLK3kXDgFTe9jgMeFYqk8nHwcWh"
base_ta = "C3FzbX9n1YD2dow2dCmEv5uNyyf22Gb3TLAEqGBhw5fY"
quote_ta = "3RWFAQBRkNGq7CMGcTLK3kXDgFTe9jgMeFYqk8nHwcWh"

## other humidifi markets

# https://solscan.io/account/DB3sUCP2H4icbeKmK6yb6nUxU5ogbcRHtGuq7W2RoRwW
# market = "DB3sUCP2H4icbeKmK6yb6nUxU5ogbcRHtGuq7W2RoRwW"
# base_token_acc = "8BrVfsvzb1DZqCactbYWoKSv24AfsLBuXJqzpzYCwznF"
# quote_token_acc = "HsQcHFFNUVTp3MWrXYbuZchBNd4Pwk8636bKzLvpfYNR"
# base_ta = "8BrVfsvzb1DZqCactbYWoKSv24AfsLBuXJqzpzYCwznF"
# quote_ta = "HsQcHFFNUVTp3MWrXYbuZchBNd4Pwk8636bKzLvpfYNR"

# https://solscan.io/account/6n9VhCwQ7EwK6NqFDjnHPzEk6wZdRBTfh43RFgHQWHuQ
# market = "6n9VhCwQ7EwK6NqFDjnHPzEk6wZdRBTfh43RFgHQWHuQ"
# base_token_acc = "C3FzbX9n1YD2dow2dCmEv5uNyyf22Gb3TLAEqGBhw5fY"
# quote_token_acc = "3RWFAQBRkNGq7CMGcTLK3kXDgFTe9jgMeFYqk8nHwcWh"
# base_ta = "C3FzbX9n1YD2dow2dCmEv5uNyyf22Gb3TLAEqGBhw5fY"
# quote_ta = "3RWFAQBRkNGq7CMGcTLK3kXDgFTe9jgMeFYqk8nHwcWh"

# market = "AvGeFw71N5sNfV97mZ1uNrHg4yfufRicCJUrS9j2ehTX"
# base_token_acc = "ECEPWwZJ1U1Vjsj1X5sUbZYETKMSCjYHuoTMVitCn64t"
# quote_token_acc = "FBWtVVvzsRuAAzVX8ua1hden9KmgPrC2rFijuwEn1ngJ"
# base_ta = "ECEPWwZJ1U1Vjsj1X5sUbZYETKMSCjYHuoTMVitCn64t"
# quote_ta = "FBWtVVvzsRuAAzVX8ua1hden9KmgPrC2rFijuwEn1ngJ"

# https://solscan.io/account/FLckHLGMJy5gEoXWwcE68Nprde1D4araK4TGLw4pQq2n
[tessera]
market = "FLckHLGMJy5gEoXWwcE68Nprde1D4araK4TGLw4pQq2n"
base_token_acc = "5pVN5XZB8cYBjNLFrsBCPWkCQBan5K5Mq2dWGzwPgGJV"
quote_token_acc = "9t4P5wMwfFkyn92Z7hf463qYKEZf8ERVZsGBEPNp8uJx"
base_ta = "5pVN5XZB8cYBjNLFrsBCPWkCQBan5K5Mq2dWGzwPgGJV"
quote_ta = "9t4P5wMwfFkyn92Z7hf463qYKEZf8ERVZsGBEPNp8uJx"
global_state = "8ekCy2jHHUbW2yeNGFWYJT9Hm9FW7SvZcZK66dSZCDiF"

# https://solscan.io/account/4uWuh9fC7rrZKrN8ZdJf69MN1e2S7FPpMqcsyY1aof6K
[goonfi]
market = "4uWuh9fC7rrZKrN8ZdJf69MN1e2S7FPpMqcsyY1aof6K"
base_token_acc = "pKiUC9hDXv52xqU1p3BKypV9AQjAMgfZUGRnoBsdkKm"
quote_token_acc = "Gsy5Zr7Vxn5KckAbduPHHGR1qzPJ4w3GSYmcinWAkhrC"
base_ta = "pKiUC9hDXv52xqU1p3BKypV9AQjAMgfZUGRnoBsdkKm"
quote_ta = "Gsy5Zr7Vxn5KckAbduPHHGR1qzPJ4w3GSYmcinWAkhrC"
blacklist = "7XqYD6DEGmDXooB1E8NNRWV9pWAmm1z6WYpsfjnABTUz"

# https://solscan.io/account/65ZHSArs5XxPseKQbB1B4r16vDxMWnCxHMzogDAqiDUc
[solfi-v2]
market = "65ZHSArs5XxPseKQbB1B4r16vDxMWnCxHMzogDAqiDUc"
base_token_acc = "CRo8DBwrmd97DJfAnvCv96tZPL5Mktf2NZy2ZnhDer1A"
quote_token_acc = "GhFfLFSprPpfoRaWakPMmJTMJBHuz6C694jYwxy2dAic"
base_ta = "CRo8DBwrmd97DJfAnvCv96tZPL5Mktf2NZy2ZnhDer1A"
quote_ta = "GhFfLFSprPpfoRaWakPMmJTMJBHuz6C694jYwxy2dAic"
cfg = "FmxXDSR9WvpJTCh738D1LEDuhMoA8geCtZgHb3isy7Dp"
oracle = "2ny7eGyZCoeEVTkNLf5HcnJFBKkyA4p4gcrtb3b8y8ou"

Expand Down
Loading
Loading