feat(pumpswap): support virtual quote reserves
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sol-trade-sdk"
|
||||
version = "4.0.22"
|
||||
version = "4.0.23"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"William <byteblock6@gmail.com>",
|
||||
|
||||
@@ -92,9 +92,9 @@ This SDK is available in multiple languages:
|
||||
|
||||
## 🔖 Current Release
|
||||
|
||||
**Rust crate:** `sol-trade-sdk = "4.0.21"`
|
||||
**Rust crate:** `sol-trade-sdk = "4.0.23"`
|
||||
|
||||
This release refreshes PumpFun native-SOL quote handling so SOL/WSOL sentinels prefer the smaller V1 hot path, keeps the default RPC submit lane active alongside SWQoS lanes, restores the fast-submit result window to 5 seconds, and aligns Raydium CPMM fixed-output swaps with the on-chain `swap_base_out` instruction. Trade execution requires a caller-supplied `recent_blockhash` or durable nonce; hot-path execution does not query RPC for blockhash, account, or balance data.
|
||||
This release updates PumpSwap for the July 2026 virtual quote reserve rollout. Pool and event schemas include `virtual_quote_reserves`, and all PumpSwap buy, sell, pricing, and dynamic-fee calculations use `quote_vault_balance + virtual_quote_reserves`.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -126,14 +126,14 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.21" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.23" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
sol-trade-sdk = "4.0.21"
|
||||
sol-trade-sdk = "4.0.23"
|
||||
```
|
||||
|
||||
## 🛠️ Usage Examples
|
||||
@@ -506,6 +506,14 @@ For **PumpSwap** (Pump AMM), `coin_creator_vault_ata` and `coin_creator_vault_au
|
||||
- **sol-parser-sdk**: Instruction parser sets them from accounts 17 and 18; the account filler also fills them when the event comes from logs. Use `PumpSwapParams::from_trade(..., e.coin_creator_vault_ata, e.coin_creator_vault_authority, ...)` with the buy/sell event `e`.
|
||||
- **solana-streamer**: Instruction parser sets them from `accounts.get(17)` and `accounts.get(18)`. Use the same `from_trade` with the event's `coin_creator_vault_ata` and `coin_creator_vault_authority`.
|
||||
|
||||
#### PumpSwap: virtual quote reserves
|
||||
|
||||
PumpSwap quotes must use `effective_quote_reserves = pool_quote_token_account.amount + virtual_quote_reserves`. The Pool account and BuyEvent/SellEvent encode `virtual_quote_reserves` as `i128`.
|
||||
|
||||
- RPC constructors such as `PumpSwapParams::from_pool_address_by_rpc` read and apply the Pool field automatically.
|
||||
- Event fast paths must pass the event's raw `pool_quote_token_reserves` and `virtual_quote_reserves` separately to `PumpSwapParams::from_trade(...)` or `from_trade_with_fee_basis_points(...)`. Do not add them before calling the constructor.
|
||||
- The SDK uses effective reserves for buys, sells, prices, and dynamic fee-tier selection. Invalid signed sums return an error instead of wrapping.
|
||||
|
||||
## 🛡️ MEV Protection Services
|
||||
|
||||
You can apply for a key through the official website: [Community Website](https://fnzero.dev/swqos)
|
||||
|
||||
+12
-4
@@ -92,9 +92,9 @@
|
||||
|
||||
## 🔖 当前版本
|
||||
|
||||
**Rust crate:** `sol-trade-sdk = "4.0.21"`
|
||||
**Rust crate:** `sol-trade-sdk = "4.0.23"`
|
||||
|
||||
本版本刷新 PumpFun native SOL quote 处理逻辑,SOL/WSOL sentinel 默认优先走更小的 V1 热路径,确保默认 RPC 提交通道会和 SWQoS 通道一起发出,快速提交结果等待窗口恢复为 5 秒,并将 Raydium CPMM fixed-output 交易对齐到链上 `swap_base_out` 指令。交易执行必须由调用方传入 `recent_blockhash` 或 durable nonce;热路径不会查询 RPC 获取 blockhash、账户或余额数据。
|
||||
本版本适配 PumpSwap 2026 年 7 月的虚拟 quote 储备升级。Pool 与事件 schema 新增 `virtual_quote_reserves`,PumpSwap 买入、卖出、报价和动态费率计算统一使用 `quote_vault_balance + virtual_quote_reserves`。
|
||||
|
||||
## ✨ 项目特性
|
||||
|
||||
@@ -126,14 +126,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.21" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.23" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = "4.0.21"
|
||||
sol-trade-sdk = "4.0.23"
|
||||
```
|
||||
|
||||
## 🛠️ 使用示例
|
||||
@@ -503,6 +503,14 @@ legacy SOL 事件里如果 `quote_mint` 是默认值或 Solscan SOL,并且 quo
|
||||
| 未设置(默认)/ `SOL_TOKEN_ACCOUNT` (`So111...11111`) / `WSOL_TOKEN_ACCOUNT` (`So111...11112`) | 优先旧版 `buy`/`sell`/`buy_exact_sol_in` | native SOL 配对;普通 SOL 结算走 V1,显式 WSOL 结算才走 V2 |
|
||||
| `USDC_TOKEN_ACCOUNT` | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | USDC 配对(必须使用 v2) |
|
||||
|
||||
#### PumpSwap:虚拟 quote 储备
|
||||
|
||||
PumpSwap 报价必须使用 `effective_quote_reserves = pool_quote_token_account.amount + virtual_quote_reserves`。Pool 账户以及 BuyEvent/SellEvent 中的 `virtual_quote_reserves` 类型均为 `i128`。
|
||||
|
||||
- `PumpSwapParams::from_pool_address_by_rpc` 等 RPC 构造器会自动读取并应用 Pool 字段。
|
||||
- 事件热路径必须把事件中的原始 `pool_quote_token_reserves` 和 `virtual_quote_reserves` 分别传给 `PumpSwapParams::from_trade(...)` 或 `from_trade_with_fee_basis_points(...)`,不要在调用前自行相加。
|
||||
- SDK 在买入、卖出、报价和动态费率分层中统一使用有效储备;无效的有符号结果会返回错误,不会发生整数回绕。
|
||||
|
||||
## 🛡️ MEV 保护服务
|
||||
|
||||
可以通过官网申请密钥:[社区官网](https://fnzero.dev/swqos)
|
||||
|
||||
@@ -21,6 +21,7 @@ cargo run --release --package pumpswap_trading
|
||||
|
||||
- The buy uses `BuyAmount::WithMaxInput`, which applies slippage to maximum quote cost and is appropriate when fill priority matters.
|
||||
- Buy parameters use post-trade reserves and LP/protocol/creator fee bps from the event.
|
||||
- `solana-streamer-sdk 0.5.0` predates the appended `virtual_quote_reserves` event field, so this compatibility example reads that Pool field once before quoting. When the parser exposes the field, pass it directly to `PumpSwapParams::from_trade_with_fee_basis_points` or keep it in a Pool account cache to remove this RPC from the hot path.
|
||||
- The example records the pre-buy balance and sells only the confirmed balance increase. It refreshes pool state and blockhash before selling.
|
||||
- Use `BuyAmount::ExactInput` when the quote spend must be exact. That mode protects minimum output and can fail more often in an active pool.
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ cargo run --release --package pumpswap_trading
|
||||
|
||||
- 买入使用 `BuyAmount::WithMaxInput`,适合优先成交的跟单/狙击场景,滑点限制最大 quote 成本。
|
||||
- 买入参数使用事件中的成交后储备和 LP/protocol/creator fee bps。
|
||||
- `solana-streamer-sdk 0.5.0` 尚未暴露追加的 `virtual_quote_reserves` 事件字段,因此该兼容示例会在报价前读取一次 Pool 字段。解析器暴露该字段后,应直接传给 `PumpSwapParams::from_trade_with_fee_basis_points`,或维护 Pool 账户缓存,以移除热路径中的这次 RPC。
|
||||
- 示例记录买前余额,只卖出确认后的余额增量;卖出前重新获取池状态和 blockhash。
|
||||
- 若业务必须精确花费 quote,应改用 `BuyAmount::ExactInput`。这会启用最小输出保护,在活跃池中更容易因状态变化而失败。
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use sol_trade_sdk::common::{clock::now_micros, SolanaRpcClient, TradeConfig};
|
||||
use sol_trade_sdk::instruction::utils::pumpswap::fetch_pool;
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
@@ -279,6 +280,10 @@ async fn pumpswap_trade_with_grpc_buy_event(
|
||||
blockhash_cache: BlockhashCache,
|
||||
trade_info: PumpSwapBuyEvent,
|
||||
) -> AnyResult<()> {
|
||||
// solana-streamer-sdk 0.5.0 predates the appended event field. Read the
|
||||
// Pool value so this compatibility example still prices effective reserves.
|
||||
let virtual_quote_reserves =
|
||||
fetch_pool(&client.infrastructure.rpc, &trade_info.pool).await?.virtual_quote_reserves;
|
||||
let params = PumpSwapParams::from_trade_with_fee_basis_points(
|
||||
trade_info.pool,
|
||||
trade_info.base_mint,
|
||||
@@ -287,6 +292,7 @@ async fn pumpswap_trade_with_grpc_buy_event(
|
||||
trade_info.pool_quote_token_account,
|
||||
trade_info.pool_base_token_reserves,
|
||||
trade_info.pool_quote_token_reserves,
|
||||
virtual_quote_reserves,
|
||||
trade_info.coin_creator_vault_ata,
|
||||
trade_info.coin_creator_vault_authority,
|
||||
trade_info.base_token_program,
|
||||
@@ -317,6 +323,8 @@ async fn pumpswap_trade_with_grpc_sell_event(
|
||||
blockhash_cache: BlockhashCache,
|
||||
trade_info: PumpSwapSellEvent,
|
||||
) -> AnyResult<()> {
|
||||
let virtual_quote_reserves =
|
||||
fetch_pool(&client.infrastructure.rpc, &trade_info.pool).await?.virtual_quote_reserves;
|
||||
let params = PumpSwapParams::from_trade_with_fee_basis_points(
|
||||
trade_info.pool,
|
||||
trade_info.base_mint,
|
||||
@@ -325,6 +333,7 @@ async fn pumpswap_trade_with_grpc_sell_event(
|
||||
trade_info.pool_quote_token_account,
|
||||
trade_info.pool_base_token_reserves,
|
||||
trade_info.pool_quote_token_reserves,
|
||||
virtual_quote_reserves,
|
||||
trade_info.coin_creator_vault_ata,
|
||||
trade_info.coin_creator_vault_authority,
|
||||
trade_info.base_token_program,
|
||||
|
||||
@@ -288,6 +288,213 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "boost_buy_and_burn",
|
||||
"discriminator": [
|
||||
105,
|
||||
68,
|
||||
6,
|
||||
175,
|
||||
0,
|
||||
7,
|
||||
35,
|
||||
162
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "pool"
|
||||
},
|
||||
{
|
||||
"name": "authority",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "global_config",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108,
|
||||
95,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
102,
|
||||
105,
|
||||
103
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_mint",
|
||||
"writable": true,
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "quote_mint",
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pool_base_token_account",
|
||||
"writable": true,
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pool_quote_token_account",
|
||||
"writable": true,
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "boost_vault_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
111,
|
||||
111,
|
||||
115,
|
||||
116,
|
||||
95,
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "pool"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "boost_vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "boost_vault_authority"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "quote_token_program"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "quote_mint"
|
||||
}
|
||||
],
|
||||
"program": {
|
||||
"kind": "const",
|
||||
"value": [
|
||||
140,
|
||||
151,
|
||||
37,
|
||||
143,
|
||||
78,
|
||||
36,
|
||||
137,
|
||||
241,
|
||||
187,
|
||||
61,
|
||||
16,
|
||||
41,
|
||||
20,
|
||||
142,
|
||||
13,
|
||||
131,
|
||||
11,
|
||||
90,
|
||||
19,
|
||||
153,
|
||||
218,
|
||||
255,
|
||||
16,
|
||||
132,
|
||||
4,
|
||||
142,
|
||||
123,
|
||||
216,
|
||||
219,
|
||||
233,
|
||||
248,
|
||||
89
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_token_program"
|
||||
},
|
||||
{
|
||||
"name": "quote_token_program"
|
||||
},
|
||||
{
|
||||
"name": "event_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
95,
|
||||
95,
|
||||
101,
|
||||
118,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
95,
|
||||
97,
|
||||
117,
|
||||
116,
|
||||
104,
|
||||
111,
|
||||
114,
|
||||
105,
|
||||
116,
|
||||
121
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "program"
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "quote_amount_in",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "min_base_amount_burned",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "buy",
|
||||
"docs": [
|
||||
@@ -2587,6 +2794,208 @@
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "init_boost",
|
||||
"discriminator": [
|
||||
140,
|
||||
233,
|
||||
33,
|
||||
94,
|
||||
132,
|
||||
90,
|
||||
194,
|
||||
143
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "pool",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "global_config",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
103,
|
||||
108,
|
||||
111,
|
||||
98,
|
||||
97,
|
||||
108,
|
||||
95,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
102,
|
||||
105,
|
||||
103
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "creator",
|
||||
"writable": true,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"name": "base_mint",
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "quote_mint",
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pool_base_token_account",
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pool_quote_token_account",
|
||||
"writable": true,
|
||||
"relations": [
|
||||
"pool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "boost_vault_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
98,
|
||||
111,
|
||||
111,
|
||||
115,
|
||||
116,
|
||||
95,
|
||||
118,
|
||||
97,
|
||||
117,
|
||||
108,
|
||||
116
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "pool"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "boost_vault",
|
||||
"writable": true,
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "boost_vault_authority"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "quote_token_program"
|
||||
},
|
||||
{
|
||||
"kind": "account",
|
||||
"path": "quote_mint"
|
||||
}
|
||||
],
|
||||
"program": {
|
||||
"kind": "const",
|
||||
"value": [
|
||||
140,
|
||||
151,
|
||||
37,
|
||||
143,
|
||||
78,
|
||||
36,
|
||||
137,
|
||||
241,
|
||||
187,
|
||||
61,
|
||||
16,
|
||||
41,
|
||||
20,
|
||||
142,
|
||||
13,
|
||||
131,
|
||||
11,
|
||||
90,
|
||||
19,
|
||||
153,
|
||||
218,
|
||||
255,
|
||||
16,
|
||||
132,
|
||||
4,
|
||||
142,
|
||||
123,
|
||||
216,
|
||||
219,
|
||||
233,
|
||||
248,
|
||||
89
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "quote_token_program"
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
},
|
||||
{
|
||||
"name": "associated_token_program",
|
||||
"address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
|
||||
},
|
||||
{
|
||||
"name": "event_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
95,
|
||||
95,
|
||||
101,
|
||||
118,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
95,
|
||||
97,
|
||||
117,
|
||||
116,
|
||||
104,
|
||||
111,
|
||||
114,
|
||||
105,
|
||||
116,
|
||||
121
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "program"
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "init_user_volume_accumulator",
|
||||
"discriminator": [
|
||||
@@ -3175,6 +3584,72 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "set_boost_authority",
|
||||
"discriminator": [
|
||||
227,
|
||||
149,
|
||||
76,
|
||||
42,
|
||||
130,
|
||||
39,
|
||||
234,
|
||||
205
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"signer": true,
|
||||
"relations": [
|
||||
"global_config"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "global_config",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"name": "boost_authority"
|
||||
},
|
||||
{
|
||||
"name": "system_program",
|
||||
"address": "11111111111111111111111111111111"
|
||||
},
|
||||
{
|
||||
"name": "event_authority",
|
||||
"pda": {
|
||||
"seeds": [
|
||||
{
|
||||
"kind": "const",
|
||||
"value": [
|
||||
95,
|
||||
95,
|
||||
101,
|
||||
118,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
95,
|
||||
97,
|
||||
117,
|
||||
116,
|
||||
104,
|
||||
111,
|
||||
114,
|
||||
105,
|
||||
116,
|
||||
121
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "program"
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "set_coin_creator",
|
||||
"docs": [
|
||||
@@ -3610,6 +4085,38 @@
|
||||
],
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "toggle_boost",
|
||||
"discriminator": [
|
||||
117,
|
||||
161,
|
||||
160,
|
||||
74,
|
||||
223,
|
||||
137,
|
||||
118,
|
||||
99
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "admin",
|
||||
"signer": true,
|
||||
"relations": [
|
||||
"global_config"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "global_config",
|
||||
"writable": true
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "enabled",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "toggle_cashback_enabled",
|
||||
"discriminator": [
|
||||
@@ -4672,6 +5179,19 @@
|
||||
222
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BoostBuyAndBurnEvent",
|
||||
"discriminator": [
|
||||
63,
|
||||
69,
|
||||
28,
|
||||
22,
|
||||
48,
|
||||
92,
|
||||
194,
|
||||
185
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BuyEvent",
|
||||
"discriminator": [
|
||||
@@ -4802,6 +5322,19 @@
|
||||
124
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "InitBoostEvent",
|
||||
"discriminator": [
|
||||
174,
|
||||
124,
|
||||
74,
|
||||
249,
|
||||
4,
|
||||
81,
|
||||
246,
|
||||
17
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "InitUserVolumeAccumulatorEvent",
|
||||
"discriminator": [
|
||||
@@ -4867,6 +5400,19 @@
|
||||
211
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SetBoostAuthorityEvent",
|
||||
"discriminator": [
|
||||
89,
|
||||
128,
|
||||
240,
|
||||
141,
|
||||
91,
|
||||
202,
|
||||
71,
|
||||
105
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SetMetaplexCoinCreatorEvent",
|
||||
"discriminator": [
|
||||
@@ -5177,6 +5723,51 @@
|
||||
{
|
||||
"code": 6058,
|
||||
"name": "BuybackFeeRecipientMissing"
|
||||
},
|
||||
{
|
||||
"code": 6059,
|
||||
"name": "MissingCashbackAccounts",
|
||||
"msg": "Cashback trade is missing the required remaining accounts"
|
||||
},
|
||||
{
|
||||
"code": 6060,
|
||||
"name": "InvalidCashbackAccumulator",
|
||||
"msg": "Cashback user_volume_accumulator account is invalid"
|
||||
},
|
||||
{
|
||||
"code": 6061,
|
||||
"name": "InvalidCashbackAccumulatorAta",
|
||||
"msg": "Cashback user_volume_accumulator ATA is missing or invalid"
|
||||
},
|
||||
{
|
||||
"code": 6062,
|
||||
"name": "InvalidPoolV2",
|
||||
"msg": "pool_v2 remaining account is missing or invalid"
|
||||
},
|
||||
{
|
||||
"code": 6063,
|
||||
"name": "InsufficientRealQuoteReserves",
|
||||
"msg": "BOOST: sell output exceeds the real quote vault. effective = real + virtual is pricing-only; payout is capped at real_vault, so quote min(out, real_vault)"
|
||||
},
|
||||
{
|
||||
"code": 6064,
|
||||
"name": "BoostPoolLiquidityUnsupported",
|
||||
"msg": "BOOST: deposit/withdraw don't apply to boost pools"
|
||||
},
|
||||
{
|
||||
"code": 6065,
|
||||
"name": "PoolCannotBoost",
|
||||
"msg": "BOOST: pool cannot be boosted (no virtual reserves)"
|
||||
},
|
||||
{
|
||||
"code": 6066,
|
||||
"name": "BoostDisabled",
|
||||
"msg": "BOOST: boost is disabled"
|
||||
},
|
||||
{
|
||||
"code": 6067,
|
||||
"name": "SeedLockViolation",
|
||||
"msg": "BOOST: lp_supply must never drop below the circulating LP mint supply"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
@@ -5292,6 +5883,62 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BoostBuyAndBurnEvent",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "timestamp",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "bonding_curve",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "pool",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "authority",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "quote_amount_in_requested",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "quote_amount_in_used",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "base_amount_burned",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "virtual_quote_reserves",
|
||||
"type": "i128"
|
||||
},
|
||||
{
|
||||
"name": "real_quote_reserves_after",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "base_reserves_after",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "boost_vault_remaining",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BuyEvent",
|
||||
"docs": [
|
||||
@@ -5435,6 +6082,18 @@
|
||||
{
|
||||
"name": "buyback_fee",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "virtual_quote_reserves",
|
||||
"type": "i128"
|
||||
},
|
||||
{
|
||||
"name": "can_boost",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "base_supply",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6018,6 +6677,14 @@
|
||||
{
|
||||
"name": "buyback_basis_points",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "boost_authority",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "boost_enabled",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6064,6 +6731,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "InitBoostEvent",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "timestamp",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "bonding_curve",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "pool",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "virtual_quote_reserves",
|
||||
"type": "i128"
|
||||
},
|
||||
{
|
||||
"name": "real_quote_reserves_after",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "InitUserVolumeAccumulatorEvent",
|
||||
"type": {
|
||||
@@ -6180,6 +6879,13 @@
|
||||
{
|
||||
"name": "is_cashback_coin",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "virtual_quote_reserves",
|
||||
"docs": [
|
||||
"For non-boost pools, value is 0, so the behavior is identical to legacy pools."
|
||||
],
|
||||
"type": "i128"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6321,6 +7027,18 @@
|
||||
{
|
||||
"name": "buyback_fee",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "virtual_quote_reserves",
|
||||
"type": "i128"
|
||||
},
|
||||
{
|
||||
"name": "can_boost",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "base_supply",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6353,6 +7071,30 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SetBoostAuthorityEvent",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "timestamp",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "old_boost_authority",
|
||||
"type": "pubkey"
|
||||
},
|
||||
{
|
||||
"name": "new_boost_authority",
|
||||
"type": "pubkey"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SetMetaplexCoinCreatorEvent",
|
||||
"type": {
|
||||
|
||||
@@ -54,7 +54,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
let base_mint = protocol_params.base_mint;
|
||||
let quote_mint = protocol_params.quote_mint;
|
||||
let pool_base_token_reserves = protocol_params.pool_base_token_reserves;
|
||||
let pool_quote_token_reserves = protocol_params.pool_quote_token_reserves;
|
||||
let pool_quote_token_reserves = protocol_params.effective_quote_reserves()?;
|
||||
let params_coin_creator_vault_ata = protocol_params.coin_creator_vault_ata;
|
||||
let params_coin_creator_vault_authority = protocol_params.coin_creator_vault_authority;
|
||||
let create_input_ata = params.create_input_mint_ata;
|
||||
@@ -283,7 +283,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
let base_mint = protocol_params.base_mint;
|
||||
let quote_mint = protocol_params.quote_mint;
|
||||
let pool_base_token_reserves = protocol_params.pool_base_token_reserves;
|
||||
let pool_quote_token_reserves = protocol_params.pool_quote_token_reserves;
|
||||
let pool_quote_token_reserves = protocol_params.effective_quote_reserves()?;
|
||||
let pool_base_token_account = protocol_params.pool_base_token_account;
|
||||
let pool_quote_token_account = protocol_params.pool_quote_token_account;
|
||||
let params_coin_creator_vault_ata = protocol_params.coin_creator_vault_ata;
|
||||
@@ -535,6 +535,7 @@ mod tests {
|
||||
pk(4),
|
||||
1_000_000_000,
|
||||
2_000_000_000,
|
||||
0,
|
||||
pk(5),
|
||||
accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY,
|
||||
crate::constants::TOKEN_PROGRAM,
|
||||
@@ -627,6 +628,7 @@ mod tests {
|
||||
pk(4),
|
||||
1_000_000_000,
|
||||
2_000_000_000,
|
||||
0,
|
||||
pk(5),
|
||||
accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY,
|
||||
crate::constants::TOKEN_PROGRAM,
|
||||
@@ -653,8 +655,9 @@ mod tests {
|
||||
let mut params = swap_params(TradeType::Buy, None);
|
||||
params.input_amount = Some(1_000_000);
|
||||
params.use_exact_sol_amount = Some(false);
|
||||
params.protocol_params =
|
||||
DexParamEnum::PumpSwap(pumpswap_params().with_fee_basis_points(20, 5, 75));
|
||||
let mut protocol_params = pumpswap_params().with_fee_basis_points(20, 5, 75);
|
||||
protocol_params.virtual_quote_reserves = 500_000_000;
|
||||
params.protocol_params = DexParamEnum::PumpSwap(protocol_params);
|
||||
|
||||
let instructions =
|
||||
PumpSwapInstructionBuilder.build_buy_instructions(¶ms).await.unwrap();
|
||||
@@ -667,10 +670,33 @@ mod tests {
|
||||
1_000_000,
|
||||
100,
|
||||
1_000_000_000,
|
||||
2_000_000_000,
|
||||
2_500_000_000,
|
||||
&crate::instruction::utils::pumpswap::PumpSwapFeeBasisPoints::new(20, 5, 0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(base_amount_out, expected.base);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn pumpswap_sell_prices_with_effective_quote_reserves() {
|
||||
let mut params = swap_params(TradeType::Sell, None);
|
||||
let mut protocol_params = pumpswap_params().with_fee_basis_points(20, 5, 0);
|
||||
protocol_params.virtual_quote_reserves = 500_000_000;
|
||||
params.protocol_params = DexParamEnum::PumpSwap(protocol_params);
|
||||
|
||||
let instructions =
|
||||
PumpSwapInstructionBuilder.build_sell_instructions(¶ms).await.unwrap();
|
||||
let ix = instructions.last().unwrap();
|
||||
let min_quote_amount_out = u64::from_le_bytes(ix.data[16..24].try_into().unwrap());
|
||||
|
||||
let expected = crate::utils::calc::pumpswap::sell_base_input_internal_with_fees(
|
||||
100_000,
|
||||
100,
|
||||
1_000_000_000,
|
||||
2_500_000_000,
|
||||
&crate::instruction::utils::pumpswap::PumpSwapFeeBasisPoints::new(20, 5, 0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(min_quote_amount_out, expected.min_quote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use std::sync::{
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::warn;
|
||||
|
||||
// Pool account sizes moved to find_by_base_mint/find_by_quote_mint (POOL_DATA_LEN_SPL, POOL_DATA_LEN_T22)
|
||||
// Pool account sizes are handled by find_by_base_mint/find_by_quote_mint.
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
@@ -740,12 +740,14 @@ pub async fn fetch_pool(
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
/// Known pool account sizes: 252 (SPL Token) and 643 (Token2022)
|
||||
const POOL_DATA_LEN_SPL: u64 = 8 + 244;
|
||||
const POOL_DATA_LEN_T22: u64 = 643;
|
||||
/// Known allocated Pool account sizes. The July 2026 layout carrying
|
||||
/// `virtual_quote_reserves` is allocated to 300 bytes on-chain.
|
||||
const POOL_DATA_LEN_LEGACY: u64 = 8 + 244;
|
||||
const POOL_DATA_LEN_CURRENT: u64 = 300;
|
||||
const POOL_DATA_LEN_EXTENDED: u64 = 643;
|
||||
|
||||
/// Run getProgramAccounts with a Memcmp filter, querying both pool sizes in parallel.
|
||||
async fn get_program_accounts_both_sizes(
|
||||
/// Run getProgramAccounts with a Memcmp filter, querying known Pool sizes in parallel.
|
||||
async fn get_program_accounts_known_sizes(
|
||||
rpc: &SolanaRpcClient,
|
||||
memcmp_offset: usize,
|
||||
mint: &Pubkey,
|
||||
@@ -768,12 +770,14 @@ async fn get_program_accounts_both_sizes(
|
||||
};
|
||||
let program_id = accounts::AMM_PROGRAM;
|
||||
#[allow(deprecated)]
|
||||
let (spl_result, t22_result) = tokio::join!(
|
||||
rpc.get_program_accounts_with_config(&program_id, make_config(POOL_DATA_LEN_SPL)),
|
||||
rpc.get_program_accounts_with_config(&program_id, make_config(POOL_DATA_LEN_T22)),
|
||||
let (legacy_result, current_result, extended_result) = tokio::join!(
|
||||
rpc.get_program_accounts_with_config(&program_id, make_config(POOL_DATA_LEN_LEGACY)),
|
||||
rpc.get_program_accounts_with_config(&program_id, make_config(POOL_DATA_LEN_CURRENT)),
|
||||
rpc.get_program_accounts_with_config(&program_id, make_config(POOL_DATA_LEN_EXTENDED)),
|
||||
);
|
||||
let mut all = spl_result.unwrap_or_default();
|
||||
all.extend(t22_result.unwrap_or_default());
|
||||
let mut all = legacy_result.unwrap_or_default();
|
||||
all.extend(current_result.unwrap_or_default());
|
||||
all.extend(extended_result.unwrap_or_default());
|
||||
Ok(all)
|
||||
}
|
||||
|
||||
@@ -797,7 +801,7 @@ pub async fn find_by_base_mint(
|
||||
base_mint: &Pubkey,
|
||||
) -> Result<(Pubkey, Pool), anyhow::Error> {
|
||||
// base_mint offset: 8(discriminator) + 1(bump) + 2(index) + 32(creator) = 43
|
||||
let accounts = get_program_accounts_both_sizes(rpc, 43, base_mint).await?;
|
||||
let accounts = get_program_accounts_known_sizes(rpc, 43, base_mint).await?;
|
||||
if accounts.is_empty() {
|
||||
return Err(anyhow!("No pool found for mint {}", base_mint));
|
||||
}
|
||||
@@ -814,7 +818,7 @@ pub async fn find_by_quote_mint(
|
||||
quote_mint: &Pubkey,
|
||||
) -> Result<(Pubkey, Pool), anyhow::Error> {
|
||||
// quote_mint offset: 8 + 1 + 2 + 32 + 32 = 75
|
||||
let accounts = get_program_accounts_both_sizes(rpc, 75, quote_mint).await?;
|
||||
let accounts = get_program_accounts_known_sizes(rpc, 75, quote_mint).await?;
|
||||
if accounts.is_empty() {
|
||||
return Err(anyhow!("No pool found for mint {}", quote_mint));
|
||||
}
|
||||
|
||||
@@ -17,16 +17,121 @@ pub struct Pool {
|
||||
pub is_mayhem_mode: bool,
|
||||
/// Whether this pool's coin has cashback enabled
|
||||
pub is_cashback_coin: bool,
|
||||
/// Reserved for future fields (pump-public-docs: pool structure = 244 bytes total)
|
||||
pub _reserved: [u8; 7],
|
||||
/// Virtual quote reserves appended to the Pool account.
|
||||
///
|
||||
/// Quotes must use `quote_vault_balance + virtual_quote_reserves`.
|
||||
pub virtual_quote_reserves: i128,
|
||||
}
|
||||
|
||||
/// Borsh 解码用的 Pool 长度。链上池为 244 字节(pump-public-docs Breaking Change),与 POOL_SIZE 一致。
|
||||
pub const POOL_SIZE: usize = 244;
|
||||
/// Minimum Borsh payload length for the current Pool layout, excluding the
|
||||
/// 8-byte Anchor account discriminator.
|
||||
pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1 + 1 + 16;
|
||||
const LEGACY_POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1 + 1;
|
||||
|
||||
#[derive(BorshDeserialize)]
|
||||
struct LegacyPool {
|
||||
pool_bump: u8,
|
||||
index: u16,
|
||||
creator: Pubkey,
|
||||
base_mint: Pubkey,
|
||||
quote_mint: Pubkey,
|
||||
lp_mint: Pubkey,
|
||||
pool_base_token_account: Pubkey,
|
||||
pool_quote_token_account: Pubkey,
|
||||
lp_supply: u64,
|
||||
coin_creator: Pubkey,
|
||||
is_mayhem_mode: bool,
|
||||
is_cashback_coin: bool,
|
||||
}
|
||||
|
||||
impl From<LegacyPool> for Pool {
|
||||
fn from(pool: LegacyPool) -> Self {
|
||||
Self {
|
||||
pool_bump: pool.pool_bump,
|
||||
index: pool.index,
|
||||
creator: pool.creator,
|
||||
base_mint: pool.base_mint,
|
||||
quote_mint: pool.quote_mint,
|
||||
lp_mint: pool.lp_mint,
|
||||
pool_base_token_account: pool.pool_base_token_account,
|
||||
pool_quote_token_account: pool.pool_quote_token_account,
|
||||
lp_supply: pool.lp_supply,
|
||||
coin_creator: pool.coin_creator,
|
||||
is_mayhem_mode: pool.is_mayhem_mode,
|
||||
is_cashback_coin: pool.is_cashback_coin,
|
||||
virtual_quote_reserves: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pool_decode(data: &[u8]) -> Option<Pool> {
|
||||
if data.len() < POOL_SIZE {
|
||||
return None;
|
||||
if data.len() >= POOL_SIZE {
|
||||
return borsh::from_slice::<Pool>(&data[..POOL_SIZE]).ok();
|
||||
}
|
||||
|
||||
if data.len() >= LEGACY_POOL_SIZE {
|
||||
return borsh::from_slice::<LegacyPool>(&data[..LEGACY_POOL_SIZE]).ok().map(Into::into);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Compute the quote reserves used by PumpSwap pricing.
|
||||
///
|
||||
/// Returns `None` when the signed sum is negative or cannot fit in a `u64`.
|
||||
#[inline]
|
||||
pub fn effective_quote_reserves(
|
||||
quote_vault_balance: u64,
|
||||
virtual_quote_reserves: i128,
|
||||
) -> Option<u64> {
|
||||
i128::from(quote_vault_balance)
|
||||
.checked_add(virtual_quote_reserves)
|
||||
.and_then(|reserves| u64::try_from(reserves).ok())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn pool_payload(virtual_quote_reserves: i128) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(POOL_SIZE);
|
||||
data.push(7);
|
||||
data.extend_from_slice(&42u16.to_le_bytes());
|
||||
for seed in 1..=6 {
|
||||
data.extend_from_slice(Pubkey::new_from_array([seed; 32]).as_ref());
|
||||
}
|
||||
data.extend_from_slice(&123_456u64.to_le_bytes());
|
||||
data.extend_from_slice(Pubkey::new_from_array([7; 32]).as_ref());
|
||||
data.push(1);
|
||||
data.push(0);
|
||||
data.extend_from_slice(&virtual_quote_reserves.to_le_bytes());
|
||||
data
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_current_pool_virtual_quote_reserves() {
|
||||
let pool = pool_decode(&pool_payload(987_654_321)).unwrap();
|
||||
|
||||
assert_eq!(pool.virtual_quote_reserves, 987_654_321);
|
||||
assert!(pool.is_mayhem_mode);
|
||||
assert!(!pool.is_cashback_coin);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_legacy_pool_with_zero_virtual_quote_reserves() {
|
||||
let mut data = pool_payload(0);
|
||||
data.truncate(LEGACY_POOL_SIZE);
|
||||
data.extend_from_slice(&[0; 7]);
|
||||
|
||||
let pool = pool_decode(&data).unwrap();
|
||||
assert_eq!(pool.virtual_quote_reserves, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_reserves_support_signed_virtual_amounts_and_reject_invalid_sums() {
|
||||
assert_eq!(effective_quote_reserves(1_000, 250), Some(1_250));
|
||||
assert_eq!(effective_quote_reserves(1_000, -250), Some(750));
|
||||
assert_eq!(effective_quote_reserves(100, -101), None);
|
||||
assert_eq!(effective_quote_reserves(u64::MAX, 1), None);
|
||||
}
|
||||
borsh::from_slice::<Pool>(&data[..POOL_SIZE]).ok()
|
||||
}
|
||||
|
||||
@@ -32,8 +32,10 @@ pub struct PumpSwapParams {
|
||||
pub pool_quote_token_account: Pubkey,
|
||||
/// Base token reserves in the pool
|
||||
pub pool_base_token_reserves: u64,
|
||||
/// Quote token reserves in the pool
|
||||
/// Raw quote-vault token balance. Pricing uses this plus [`Self::virtual_quote_reserves`].
|
||||
pub pool_quote_token_reserves: u64,
|
||||
/// Signed virtual quote reserves from the PumpSwap Pool account or trade event.
|
||||
pub virtual_quote_reserves: i128,
|
||||
/// Coin creator vault ATA
|
||||
pub coin_creator_vault_ata: Pubkey,
|
||||
/// Coin creator vault authority
|
||||
@@ -75,6 +77,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account: Pubkey,
|
||||
pool_base_token_reserves: u64,
|
||||
pool_quote_token_reserves: u64,
|
||||
virtual_quote_reserves: i128,
|
||||
coin_creator_vault_ata: Pubkey,
|
||||
coin_creator_vault_authority: Pubkey,
|
||||
base_token_program: Pubkey,
|
||||
@@ -99,6 +102,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
virtual_quote_reserves,
|
||||
coin_creator_vault_ata,
|
||||
coin_creator_vault_authority,
|
||||
base_token_program,
|
||||
@@ -127,6 +131,21 @@ impl PumpSwapParams {
|
||||
self
|
||||
}
|
||||
|
||||
/// Quote reserves used by PumpSwap pricing and fee-tier selection.
|
||||
pub fn effective_quote_reserves(&self) -> Result<u64, anyhow::Error> {
|
||||
crate::instruction::utils::pumpswap_types::effective_quote_reserves(
|
||||
self.pool_quote_token_reserves,
|
||||
self.virtual_quote_reserves,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Invalid PumpSwap effective quote reserves: vault={} virtual={}",
|
||||
self.pool_quote_token_reserves,
|
||||
self.virtual_quote_reserves
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_fee_basis_points(
|
||||
mut self,
|
||||
lp_fee_basis_points: u64,
|
||||
@@ -161,6 +180,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account: Pubkey,
|
||||
pool_base_token_reserves: u64,
|
||||
pool_quote_token_reserves: u64,
|
||||
virtual_quote_reserves: i128,
|
||||
coin_creator_vault_ata: Pubkey,
|
||||
coin_creator_vault_authority: Pubkey,
|
||||
base_token_program: Pubkey,
|
||||
@@ -178,6 +198,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
virtual_quote_reserves,
|
||||
coin_creator_vault_ata,
|
||||
coin_creator_vault_authority,
|
||||
base_token_program,
|
||||
@@ -202,6 +223,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account: Pubkey,
|
||||
pool_base_token_reserves: u64,
|
||||
pool_quote_token_reserves: u64,
|
||||
virtual_quote_reserves: i128,
|
||||
coin_creator_vault_ata: Pubkey,
|
||||
coin_creator_vault_authority: Pubkey,
|
||||
base_token_program: Pubkey,
|
||||
@@ -223,6 +245,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
virtual_quote_reserves,
|
||||
coin_creator_vault_ata,
|
||||
coin_creator_vault_authority,
|
||||
base_token_program,
|
||||
@@ -277,6 +300,18 @@ impl PumpSwapParams {
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let (pool_base_token_reserves, pool_quote_token_reserves) =
|
||||
crate::instruction::utils::pumpswap::get_token_balances(pool_data, rpc).await?;
|
||||
let effective_quote_token_reserves =
|
||||
crate::instruction::utils::pumpswap_types::effective_quote_reserves(
|
||||
pool_quote_token_reserves,
|
||||
pool_data.virtual_quote_reserves,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Invalid PumpSwap effective quote reserves: vault={} virtual={}",
|
||||
pool_quote_token_reserves,
|
||||
pool_data.virtual_quote_reserves
|
||||
)
|
||||
})?;
|
||||
let base_mint_supply = fetch_mint_supply(rpc, &pool_data.base_mint).await.ok();
|
||||
let fee_config = crate::instruction::utils::pumpswap::fetch_fee_config(rpc).await;
|
||||
let raw_fee_basis_points = crate::instruction::utils::pumpswap::compute_fee_basis_points(
|
||||
@@ -285,7 +320,7 @@ impl PumpSwapParams {
|
||||
pool_data.base_mint,
|
||||
base_mint_supply,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
effective_quote_token_reserves,
|
||||
);
|
||||
let creator_fee_basis_points = if pool_data.coin_creator == Pubkey::default() {
|
||||
0
|
||||
@@ -319,6 +354,7 @@ impl PumpSwapParams {
|
||||
pool_quote_token_account: pool_data.pool_quote_token_account,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
virtual_quote_reserves: pool_data.virtual_quote_reserves,
|
||||
coin_creator_vault_ata,
|
||||
coin_creator_vault_authority,
|
||||
base_token_program: if pool_data.pool_base_token_account == base_token_program_ata {
|
||||
|
||||
Reference in New Issue
Block a user