diff --git a/.claude/skills/run-mainnet-e2e-tests/references/manual-test-groups.md b/.claude/skills/run-mainnet-e2e-tests/references/manual-test-groups.md index 81b854a..3351c75 100644 --- a/.claude/skills/run-mainnet-e2e-tests/references/manual-test-groups.md +++ b/.claude/skills/run-mainnet-e2e-tests/references/manual-test-groups.md @@ -46,7 +46,7 @@ Validate: each returns valid JSON arrays, non-empty (except search which may be Find an active bonding-curve mint and a graduated mint from the discovery results above. ```bash -uv run pumpfun info # graduated=false, price_sol > 0 +uv run pumpfun info # graduated=false, price > 0, quote_symbol set uv run pumpfun info # graduated=true, pool_address present uv run pumpfun info 11111111111111111111111111111111 # should error ``` @@ -92,16 +92,16 @@ uv run pumpfun sell all --confirm - Add a ~5 second delay between buy and sell -- immediate sell after buy often fails with "No tokens to sell" due to RPC state lag (ISSUE-3) - If sell fails, retry after a few seconds -- this is a known timing issue, not a code bug -## Group 7: PumpSwap AMM Trading (SPENDS SOL -- CURRENTLY BROKEN) - -**Known bug:** PumpSwap buys consistently fail with error 6023 (NotEnoughTokensToSell). Test anyway to check if it has been fixed. +## Group 7: PumpSwap AMM Trading (SPENDS SOL) ```bash uv run pumpfun buy 0.001 --force-amm --confirm uv run pumpfun buy 0.001 --confirm # auto-route +uv run pumpfun sell all --confirm ``` -If still failing with 6023, document and move on. +A buy on a cashback pool exceeds a v0 transaction and is sent as v1; a failure +there points at the RPC endpoint refusing v1 sends, not at the instruction. ## Group 8: Transaction Status (safe, read-only RPC) @@ -123,6 +123,8 @@ uv run pumpfun claim-cashback --confirm # may succeed or be no-op uv run pumpfun close-volume-acc --confirm # may succeed or be no-op uv run pumpfun collect-creator-fee --confirm # likely "No creator fees" uv run pumpfun migrate --confirm # should error: "not complete" +uv run pumpfun --json launch --name X --ticker X --uri https://example.com/token-v2.json --creator-fee-bps 100 # should error: fee needs a QuoteControl mint +uv run pumpfun --json launch --name X --ticker X --uri https://example.com/token-v2.json --mayhem --quote-mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v # should error: mayhem needs SOL ``` ## Group 10: Wallet Cleanup (SPENDS SOL -- recovers rent) diff --git a/CLAUDE.md b/CLAUDE.md index 6694968..35a4de4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,7 +53,7 @@ src/pumpfun_cli/ ├── output.py # render() + error() — TTY-aware output ├── commands/ # Thin CLI layer (config, info, launch, tokens, trade, tx_status, wallet) ├── core/ # Business logic (config, info, launch, pumpswap, tokens, trade, tx_status, validate, wallet) -└── protocol/ # Solana primitives (address, client, contracts, curve, idl_parser, instructions, pumpswap) +└── protocol/ # Solana primitives (address, client, contracts, curve, idl_parser, instructions, pumpswap, quote) tests/ ├── test_commands/ # CLI smoke tests @@ -66,7 +66,7 @@ tests/ **Output:** Use `render(data, json_mode)` for all output — returns `True` if it emitted JSON (non-TTY or `--json`), `False` in TTY mode. Commands use `if not render(...):` to branch into human-readable output. Use `error(msg, hint, exit_code)` for failures — prints to stderr and raises `SystemExit`. Never use `print()`. -**Error handling:** `error()` raises `SystemExit` — code after it is unreachable. Core functions return `dict` with `"error"` key for expected failures (graduated tokens, not found, slippage exceeded, insufficient_balance). Catch `ValueError` for wrong wallet password in every command that decrypts. Buy/sell functions perform pre-trade balance validation — SOL balance for buys (including fees + ATA rent), token balance for sells with specific amounts. +**Error handling:** `error()` raises `SystemExit` — code after it is unreachable. Core functions return `dict` with `"error"` key for expected failures (graduated tokens, not found, slippage exceeded, insufficient_balance). Catch `ValueError` for wrong wallet password in every command that decrypts. Buy/sell functions perform pre-trade balance validation — SOL for fees and rent, the quote asset for the spend, token balance for sells with specific amounts. **Auto-routing:** When `buy_token` / `sell_token` returns `{"error": "graduated"}`, the command layer in `commands/trade.py` automatically retries via `buy_pumpswap` / `sell_pumpswap` (PumpSwap AMM). Never suppress or swallow the `"graduated"` error in core — the routing decision belongs in the command layer. @@ -101,19 +101,42 @@ from pumpfun_cli.output import render, error ## Working with Solana Accounts ```python -# Derive PDA from pumpfun_cli.protocol.address import derive_bonding_curve - -# Fetch and decode -data = await client.get_account_data(address) -state = idl.decode_account_data(data, "BondingCurve", skip_discriminator=True) - -# Build instructions -from pumpfun_cli.protocol.instructions import build_buy_exact_sol_in_instructions -ixs = build_buy_exact_sol_in_instructions(idl=idl, mint=mint, user=keypair.pubkey(), ...) -sig = await client.send_tx(ixs, [keypair], compute_units=..., confirm=True) +from pumpfun_cli.protocol.curve import parse_bonding_curve +from pumpfun_cli.protocol.instructions import build_buy_exact_quote_in_v2_instruction +from pumpfun_cli.protocol.quote import get_mint_info + +resp = await client.get_account_info(derive_bonding_curve(mint)) +state = parse_bonding_curve(bytes(resp.value.data)) +quote_program, quote_decimals = await get_mint_info(client, state["quote_mint"]) +ix = build_buy_exact_quote_in_v2_instruction(mint=mint, creator=state["creator"], ...) +sig = await client.send_tx([ix], [keypair], compute_units=..., confirm=True) ``` +## Protocol rules + +Read `docs/pumpfun-protocol.md` in pumpfun-bonkfun-bot before changing account +lists; these are the ones this CLI depends on. + +- **Amounts are in the coin's quote asset**, read from `BondingCurve.quote_mint` + (all zeros means SOL; instructions take wrapped SOL there). Its decimals come + from the mint via `get_mint_info`; never assume 9. +- Bonding-curve trades use `buy_exact_quote_in_v2` / `sell_v2`: 27 / 26 + accounts, identical for every coin. A SOL coin's quote accounts are only + seed-checked, so don't create the user's WSOL ATA for them. +- `creator_vault` derives from `BondingCurve.creator` as stored. On holder-reward + and fee-sharing coins that is not the wallet that launched the coin. +- PumpSwap swaps carry accounts the IDL omits: cashback account(s) on a cashback + pool, then `pool-v2` **only on a canonical pool** (`coin_creator` set), then the + buyback recipient and its quote ATA last. The canonical pool is derived from the + mint and the curve's quote mint. +- `send_tx` sends v0 and falls back to v1 when the transaction exceeds 1232 + bytes (a cashback-pool buy, a launch with a buy). v1 carries the compute budget + in its header. +- `create_v2` rejects cashback (6082). A creator fee only applies on a coin + priced in a `QuoteControl` mint, and mayhem coins must be SOL-priced; launch + refuses both before sending. + ## Environment ```bash diff --git a/README.md b/README.md index 372eb32..68158ae 100644 --- a/README.md +++ b/README.md @@ -65,10 +65,19 @@ pumpfun sell all # Check if a transaction landed pumpfun tx-status -# Launch a token -pumpfun launch --name "MyToken" --ticker "MTK" --desc "A great token" +# Launch a token, buying it in the same transaction +pumpfun launch --name "MyToken" --ticker "MTK" --uri https://example.com/mytoken.json --buy 0.1 ``` +pump.fun no longer accepts anonymous metadata uploads, so `launch` takes the +URI of a metadata JSON you host (`name`, `symbol`, `description`, `image`). +Without `--uri` it still tries the upload and says so when it is refused. + +Amounts are in the coin's quote asset. Most pump.fun coins are priced in SOL, +but a coin can be priced in USDC or another admitted mint; `buy`, `sell` and +`info` read it from the coin and label their output with it. Buying such a coin +spends that asset, so the wallet has to hold it. + ## Commands ``` @@ -83,18 +92,20 @@ Token info (requires RPC): pumpfun info Price, bonding progress, reserves Trading (requires RPC + wallet): - pumpfun buy [--slippage N] [--dry-run] [--confirm] - pumpfun sell [--slippage N] [--dry-run] [--confirm] - pumpfun migrate [--slippage N] [--confirm] - pumpfun claim-cashback [--confirm] - pumpfun collect-creator-fee [--confirm] - pumpfun close-volume-acc [--confirm] + pumpfun buy [--slippage N] [--force-amm] [--dry-run] [--confirm] + pumpfun sell [--slippage N] [--force-amm] [--dry-run] [--confirm] + pumpfun migrate [--confirm] + pumpfun claim-cashback [--quote-mint M] [--confirm] + pumpfun collect-creator-fee [--quote-mint M] [--confirm] + pumpfun close-volume-acc [--confirm] Transaction inspection (requires RPC): pumpfun tx-status Confirmation status, slot, fee, errors Token launch (requires RPC + wallet): - pumpfun launch --name X --ticker Y --desc Z [--image path] [--buy N] + pumpfun launch --name X --ticker Y [--uri U | --desc D --image P] [--buy N] + [--quote-mint M] [--creator-fee-bps N] [--holder-reward] + [--mayhem] [--slippage N] [--confirm] Wallet management: pumpfun wallet create Generate + encrypt keypair @@ -131,7 +142,7 @@ Global options: | `info` | Solana RPC (`getAccountInfo` for bonding curve PDA) | JSON-RPC | | `buy` | Solana RPC (`getAccountInfo`, `getLatestBlockhash`, `sendTransaction`) | JSON-RPC | | `sell` | Solana RPC (`getAccountInfo`, `getTokenAccountBalance`, `getLatestBlockhash`, `sendTransaction`) | JSON-RPC | -| `launch` | `pump.fun/api/ipfs` (metadata upload) + Solana RPC (`sendTransaction`) | HTTPS + JSON-RPC | +| `launch` | Solana RPC (`sendTransaction`); `pump.fun/api/ipfs` only without `--uri` | JSON-RPC (+ HTTPS) | | `tx-status` | Solana RPC (`getTransaction`) | JSON-RPC | | `migrate` | Solana RPC (`sendTransaction`) | JSON-RPC | | `claim-cashback` | Solana RPC (`sendTransaction`) | JSON-RPC | diff --git a/idl/pump_fun_amm_idl.json b/idl/pump_fun_amm_idl.json index 47d7b95..5aaae77 100644 --- a/idl/pump_fun_amm_idl.json +++ b/idl/pump_fun_amm_idl.json @@ -8,27 +8,22 @@ }, "instructions": [ { - "name": "admin_set_coin_creator", - "docs": [ - "Overrides the coin creator for a canonical pump pool" - ], + "name": "admin_cto_pool", "discriminator": [ - 242, - 40, - 117, - 145, - 73, - 96, - 105, - 104 + 45, + 61, + 165, + 151, + 104, + 0, + 49, + 189 ], "accounts": [ { - "name": "admin_set_coin_creator_authority", - "signer": true, - "relations": [ - "global_config" - ] + "name": "payer", + "writable": true, + "signer": true }, { "name": "global_config" @@ -37,6 +32,79 @@ "name": "pool", "writable": true }, + { + "name": "pool_authority", + "signer": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + }, + { + "kind": "account", + "path": "pool.base_mint", + "account": "Pool" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, { "name": "event_authority", "pda": { @@ -74,6 +142,16 @@ { "name": "coin_creator", "type": "pubkey" + }, + { + "name": "is_holder_reward", + "type": "bool" + }, + { + "name": "creator_fee_bps", + "type": { + "option": "u64" + } } ] }, @@ -289,36 +367,54 @@ ] }, { - "name": "buy", - "docs": [ - "For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].", - "If provided and valid, the ATA will be initialized if needed." - ], + "name": "boost_buy_and_burn", "discriminator": [ - 102, + 105, + 68, 6, - 61, - 18, - 1, - 218, - 235, - 234 + 175, + 0, + 7, + 35, + 162 ], "accounts": [ { - "name": "pool", - "writable": true + "name": "pool" }, { - "name": "user", + "name": "authority", "writable": true, "signer": true }, { - "name": "global_config" + "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" ] @@ -329,14 +425,6 @@ "pool" ] }, - { - "name": "user_base_token_account", - "writable": true - }, - { - "name": "user_quote_token_account", - "writable": true - }, { "name": "pool_base_token_account", "writable": true, @@ -352,16 +440,40 @@ ] }, { - "name": "protocol_fee_recipient" + "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": "protocol_fee_recipient_token_account", + "name": "boost_vault", "writable": true, "pda": { "seeds": [ { "kind": "account", - "path": "protocol_fee_recipient" + "path": "boost_vault_authority" }, { "kind": "account", @@ -417,14 +529,6 @@ { "name": "quote_token_program" }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, { "name": "event_authority", "pda": { @@ -455,68 +559,249 @@ } }, { - "name": "program", - "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" - }, + "name": "program" + } + ], + "args": [ { - "name": "coin_creator_vault_ata", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "coin_creator_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_amount_in", + "type": "u64" }, { - "name": "coin_creator_vault_authority", + "name": "min_base_amount_burned", + "type": "u64" + } + ] + }, + { + "name": "buy", + "docs": [ + "For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].", + "If provided and valid, the ATA will be initialized if needed." + ], + "discriminator": [ + 102, + 6, + 61, + 18, + 1, + 218, + 235, + 234 + ], + "accounts": [ + { + "name": "pool", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "global_config" + }, + { + "name": "base_mint", + "relations": [ + "pool" + ] + }, + { + "name": "quote_mint", + "relations": [ + "pool" + ] + }, + { + "name": "user_base_token_account", + "writable": true + }, + { + "name": "user_quote_token_account", + "writable": true + }, + { + "name": "pool_base_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "pool_quote_token_account", + "writable": true, + "relations": [ + "pool" + ] + }, + { + "name": "protocol_fee_recipient" + }, + { + "name": "protocol_fee_recipient_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "protocol_fee_recipient" + }, + { + "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": "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", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_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": "coin_creator_vault_authority", "pda": { "seeds": [ { @@ -1142,6 +1427,10 @@ }, { "name": "claim_cashback", + "docs": [ + "Pays out the user's accrued cashback. `user_wsol_token_account` may be any token account", + "of `quote_mint` owned by `user`, not only the associated one." + ], "discriminator": [ 37, 58, @@ -1262,67 +1551,14 @@ }, { "name": "user_wsol_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "user" - }, - { - "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": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "event_authority", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", "pda": { "seeds": [ { @@ -2319,6 +2555,30 @@ "name": "OptionBool" } } + }, + { + "name": "creator_fee_bps", + "type": { + "defined": { + "name": "OptionU64" + } + } + }, + { + "name": "can_edit_creator_fee", + "type": { + "defined": { + "name": "OptionBool" + } + } + }, + { + "name": "is_holder_reward", + "type": { + "defined": { + "name": "OptionBool" + } + } } ] }, @@ -2546,6 +2806,7 @@ }, { "name": "user", + "writable": true, "signer": true }, { @@ -2588,70 +2849,172 @@ "args": [] }, { - "name": "init_user_volume_accumulator", + "name": "init_boost", "discriminator": [ + 140, + 233, + 33, 94, - 6, - 202, - 115, - 255, - 96, - 232, - 183 + 132, + 90, + 194, + 143 ], "accounts": [ { - "name": "payer", + "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": "user" + "name": "base_mint", + "relations": [ + "pool" + ] }, { - "name": "user_volume_accumulator", + "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": [ - 117, + 98, + 111, + 111, 115, - 101, - 114, + 116, 95, 118, - 111, - 108, - 117, - 109, - 101, - 95, 97, - 99, - 99, - 117, - 109, 117, 108, - 97, - 116, - 111, - 114 + 116 ] }, { "kind": "account", - "path": "user" + "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": { @@ -2688,31 +3051,131 @@ "args": [] }, { - "name": "migrate_pool_coin_creator", - "docs": [ - "Migrate Pool Coin Creator to Sharing Config" - ], + "name": "init_user_volume_accumulator", "discriminator": [ - 208, - 8, - 159, - 4, - 74, - 175, - 16, - 58 + 94, + 6, + 202, + 115, + 255, + 96, + 232, + 183 ], "accounts": [ { - "name": "pool", + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "user" + }, + { + "name": "user_volume_accumulator", "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 112, - 111, + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "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": "migrate_pool_coin_creator", + "docs": [ + "Migrate Pool Coin Creator to Sharing Config" + ], + "discriminator": [ + 208, + 8, + 159, + 4, + 74, + 175, + 16, + 58 + ], + "accounts": [ + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, 111, 108 ] @@ -3175,6 +3638,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 +4139,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": [ @@ -3864,72 +4425,441 @@ } }, { - "name": "pump_creator_vault", - "writable": true, + "name": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "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": "transfer_creator_fees_to_pump_v2", + "discriminator": [ + 1, + 33, + 78, + 185, + 33, + 67, + 44, + 92 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "quote_mint" + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "coin_creator" + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ] + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "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": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "coin_creator" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pump_creator_vault" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_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": "update_admin", + "discriminator": [ + 161, + 176, + 40, + 213, + 60, + 184, + 179, + 228 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, + { + "name": "new_admin" + }, + { + "name": "event_authority", "pda": { "seeds": [ { "kind": "const", "value": [ - 99, - 114, + 95, + 95, + 101, + 118, 101, + 110, + 116, + 95, 97, + 117, 116, + 104, 111, 114, - 45, - 118, - 97, - 117, - 108, - 116 + 105, + 116, + 121 ] - }, - { - "kind": "account", - "path": "coin_creator" } - ], - "program": { - "kind": "const", - "value": [ - 1, - 86, - 224, - 246, - 147, - 102, - 90, - 207, - 68, - 219, - 21, - 104, - 191, - 23, - 91, - 170, - 81, - 137, - 203, - 151, - 245, - 210, - 255, - 59, - 101, - 93, - 43, - 182, - 253, - 109, - 24, - 176 - ] - } + ] } }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "update_buyback_config", + "discriminator": [ + 251, + 224, + 171, + 146, + 160, + 26, + 113, + 233 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "global_config" + ] + }, + { + "name": "global_config", + "writable": true + }, { "name": "event_authority", "pda": { @@ -3963,23 +4893,31 @@ "name": "program" } ], - "args": [] + "args": [ + { + "name": "buyback_basis_points", + "type": { + "option": "u64" + } + } + ] }, { - "name": "update_admin", + "name": "update_creator_fee_config", "discriminator": [ - 161, - 176, - 40, - 213, - 60, - 184, - 179, - 228 + 61, + 175, + 160, + 249, + 66, + 66, + 136, + 175 ], "accounts": [ { "name": "admin", + "writable": true, "signer": true, "relations": [ "global_config" @@ -3990,7 +4928,8 @@ "writable": true }, { - "name": "new_admin" + "name": "system_program", + "address": "11111111111111111111111111111111" }, { "name": "event_authority", @@ -4025,7 +4964,16 @@ "name": "program" } ], - "args": [] + "args": [ + { + "name": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" + } + ] }, { "name": "update_fee_config", @@ -4333,16 +5281,16 @@ ], "events": [ { - "name": "AdminSetCoinCreatorEvent", + "name": "AdminCtoPoolEvent", "discriminator": [ - 45, - 220, - 93, - 24, - 25, - 97, - 172, - 104 + 47, + 35, + 163, + 249, + 150, + 157, + 147, + 122 ] }, { @@ -4358,6 +5306,19 @@ 222 ] }, + { + "name": "BoostBuyAndBurnEvent", + "discriminator": [ + 63, + 69, + 28, + 22, + 48, + 92, + 194, + 185 + ] + }, { "name": "BuyEvent", "discriminator": [ @@ -4488,6 +5449,19 @@ 124 ] }, + { + "name": "InitBoostEvent", + "discriminator": [ + 174, + 124, + 74, + 249, + 4, + 81, + 246, + 17 + ] + }, { "name": "InitUserVolumeAccumulatorEvent", "discriminator": [ @@ -4553,6 +5527,19 @@ 211 ] }, + { + "name": "SetBoostAuthorityEvent", + "discriminator": [ + 89, + 128, + 240, + 141, + 91, + 202, + 71, + 105 + ] + }, { "name": "SetMetaplexCoinCreatorEvent", "discriminator": [ @@ -4592,6 +5579,19 @@ 234 ] }, + { + "name": "UpdateCreatorFeeConfigEvent", + "discriminator": [ + 152, + 198, + 124, + 124, + 106, + 246, + 127, + 191 + ] + }, { "name": "UpdateFeeConfigEvent", "discriminator": [ @@ -4811,36 +5811,158 @@ "name": "InvalidSharingConfigCoinCreator" }, { - "code": 6047, - "name": "CoinCreatorMigratedToSharingConfig", - "msg": "coin creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead" + "code": 6047, + "name": "CoinCreatorMigratedToSharingConfig", + "msg": "coin creator has been migrated to sharing config" + }, + { + "code": 6048, + "name": "CreatorVaultMigratedToSharingConfig", + "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" + }, + { + "code": 6049, + "name": "CashbackNotEnabled", + "msg": "Cashback is disabled" + }, + { + "code": 6050, + "name": "OnlyPumpPoolsCashback" + }, + { + "code": 6051, + "name": "CashbackNotInDesiredState" + }, + { + "code": 6052, + "name": "TokensInVaultLessThanCashbackEarned" + }, + { + "code": 6053, + "name": "BuybackFeeRecipientNotAuthorized", + "msg": "Buyback fee recipient not authorized" + }, + { + "code": 6054, + "name": "AllBuybackFeeRecipientsShouldBeNonZero" + }, + { + "code": 6055, + "name": "NotUniqueBuybackFeeRecipients" + }, + { + "code": 6056, + "name": "BuybackBasisPointsOutOfRange", + "msg": "buyback_basis_points must be <= 10_000" + }, + { + "code": 6057, + "name": "WrongBuybackFeeRecipientsCount", + "msg": "buyback fee recipients require exactly 8 remaining accounts (or none)" + }, + { + "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" + }, + { + "code": 6068, + "name": "CreatorFeeNotConfigurable", + "msg": "Configurable creator fee is disabled" + }, + { + "code": 6069, + "name": "CreatorFeeBpsOutOfRange", + "msg": "Creator fee basis points must be between 1 and the configured maximum" + }, + { + "code": 6070, + "name": "CreatorFeeNotEditable", + "msg": "Creator fee is not editable for this pool" + }, + { + "code": 6071, + "name": "CreatorFeeNotAllowedForCashbackCoin", + "msg": "Cashback coins cannot have a creator fee" + }, + { + "code": 6072, + "name": "SharingConfigNotActive", + "msg": "Sharing config is not active" }, { - "code": 6048, - "name": "CreatorVaultMigratedToSharingConfig", - "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" + "code": 6073, + "name": "NotAuthorized", + "msg": "Not authorized" }, { - "code": 6049, - "name": "CashbackNotEnabled", - "msg": "Cashback is disabled" + "code": 6074, + "name": "HolderRewardCreatorImmutable", + "msg": "The coin creator of a holder-reward pool cannot be changed" }, { - "code": 6050, - "name": "OnlyPumpPoolsCashback" + "code": 6075, + "name": "CtoNotAllowedForMayhemPool", + "msg": "CTO is not allowed on a mayhem-mode pool" }, { - "code": 6051, - "name": "CashbackNotInDesiredState" + "code": 6076, + "name": "InvalidHolderRewardCoinCreator", + "msg": "A holder-reward pool's coin creator must be the holder-rewards PDA" }, { - "code": 6052, - "name": "CashbackEarnedDoesNotMatchTokenInVault" + "code": 6077, + "name": "CreatorFeeNotConfigurableForQuote", + "msg": "Creator fee is not configurable on a SOL or USDC quote; the fee schedule applies" } ], "types": [ { - "name": "AdminSetCoinCreatorEvent", + "name": "AdminCtoPoolEvent", "type": { "kind": "struct", "fields": [ @@ -4848,10 +5970,6 @@ "name": "timestamp", "type": "i64" }, - { - "name": "admin_set_coin_creator_authority", - "type": "pubkey" - }, { "name": "base_mint", "type": "pubkey" @@ -4867,6 +5985,22 @@ { "name": "new_coin_creator", "type": "pubkey" + }, + { + "name": "is_holder_reward", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "old_creator_fee_bps", + "type": "u64" + }, + { + "name": "new_creator_fee_bps", + "type": "u64" } ] } @@ -4951,6 +6085,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": [ @@ -5086,6 +6276,34 @@ { "name": "cashback", "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "can_boost", + "type": "bool" + }, + { + "name": "base_supply", + "type": "u64" + }, + { + "name": "holder_rewards_bps", + "type": "u64" + }, + { + "name": "holder_rewards", + "type": "u64" } ] } @@ -5353,6 +6571,18 @@ { "name": "is_mayhem_mode", "type": "bool" + }, + { + "name": "creator_fee_bps", + "type": "u64" + }, + { + "name": "can_edit_creator_fee", + "type": "bool" + }, + { + "name": "is_holder_reward", + "type": "bool" } ] } @@ -5523,6 +6753,24 @@ } } } + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "exotic_flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } } ] } @@ -5646,6 +6894,35 @@ { "name": "is_cashback_enabled", "type": "bool" + }, + { + "name": "buyback_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "buyback_basis_points", + "type": "u64" + }, + { + "name": "boost_authority", + "type": "pubkey" + }, + { + "name": "boost_enabled", + "type": "bool" + }, + { + "name": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" } ] } @@ -5692,6 +6969,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": { @@ -5753,6 +7062,15 @@ ] } }, + { + "name": "OptionU64", + "type": { + "kind": "struct", + "fields": [ + "u64" + ] + } + }, { "name": "Pool", "type": { @@ -5808,6 +7126,25 @@ { "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" + }, + { + "name": "creator_fee_bps", + "type": "u64" + }, + { + "name": "can_edit_creator_fee", + "type": "bool" + }, + { + "name": "is_holder_reward", + "type": "bool" } ] } @@ -5941,6 +7278,34 @@ { "name": "cashback", "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + }, + { + "name": "can_boost", + "type": "bool" + }, + { + "name": "base_supply", + "type": "u64" + }, + { + "name": "holder_rewards_bps", + "type": "u64" + }, + { + "name": "holder_rewards", + "type": "u64" } ] } @@ -5973,6 +7338,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": { @@ -6107,6 +7496,30 @@ ] } }, + { + "name": "UpdateCreatorFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" + } + ] + } + }, { "name": "UpdateFeeConfigEvent", "type": { diff --git a/idl/pump_fun_fees_idl.json b/idl/pump_fun_fees_idl.json index e69de29..e740baa 100644 --- a/idl/pump_fun_fees_idl.json +++ b/idl/pump_fun_fees_idl.json @@ -0,0 +1,6482 @@ +{ + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", + "metadata": { + "name": "pump_fees", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "admin_cto_sharing_config", + "discriminator": [ + 60, + 100, + 144, + 156, + 64, + 138, + 182, + 37 + ], + "accounts": [ + { + "name": "pool_authority", + "signer": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "authority", + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "sharing_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "new_admin", + "type": { + "option": "pubkey" + } + } + ] + }, + { + "name": "claim_social_fee_pda", + "discriminator": [ + 225, + 21, + 251, + 133, + 161, + 30, + 199, + 226 + ], + "accounts": [ + { + "name": "recipient", + "writable": true + }, + { + "name": "social_fee_pda", + "writable": true + }, + { + "name": "fee_program_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "social_claim_authority", + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "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": "user_id", + "type": "string" + }, + { + "name": "platform", + "type": "u8" + } + ], + "returns": { + "option": { + "defined": { + "name": "SocialFeePdaClaimed" + } + } + } + }, + { + "name": "claim_social_fee_pda_v2", + "discriminator": [ + 17, + 77, + 240, + 134, + 58, + 188, + 53, + 149 + ], + "accounts": [ + { + "name": "recipient", + "writable": true + }, + { + "name": "social_fee_pda", + "writable": true + }, + { + "name": "quote_mint", + "docs": [ + "Quote mint for claim" + ], + "writable": true + }, + { + "name": "associated_social_fee_pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "social_fee_pda" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "associated_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "recipient" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_program_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "social_claim_authority", + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "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": "user_id", + "type": "string" + }, + { + "name": "platform", + "type": "u8" + } + ], + "returns": { + "option": { + "defined": { + "name": "SocialFeePdaClaimed" + } + } + } + }, + { + "name": "crank_donation_fee_pda", + "discriminator": [ + 220, + 10, + 189, + 167, + 169, + 17, + 25, + 69 + ], + "accounts": [ + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "payer", + "docs": [ + "Pays rent when [`temp_wsol_token_account`] is created (`init_if_needed`); receives rent when it is closed after the relay CPI." + ], + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "fee_program_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "donation_fee_pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 100, + 111, + 110, + 97, + 116, + 105, + 111, + 110, + 45, + 102, + 101, + 101, + 45, + 112, + 100, + 97 + ] + }, + { + "kind": "account", + "path": "donation_fee_pda.base_mint", + "account": "DonationFeePda" + }, + { + "kind": "account", + "path": "donation_fee_pda.config_id", + "account": "DonationFeePda" + } + ] + } + }, + { + "name": "quote_mint", + "docs": [ + "Quote mint from donation fee pda." + ], + "writable": true + }, + { + "name": "donation_fee_pda_ata", + "docs": [ + "WSOL ATA owned by `donation_fee_pda`." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "donation_fee_pda" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "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": "donation_relay_program", + "address": "RLAYHr9TRFcKB2ubYQhspcnXiaGpaVzNQvHytt47RZu" + }, + { + "name": "donation_relay_event_authority" + }, + { + "name": "mint_whitelist" + }, + { + "name": "epoch_tracker", + "writable": true + }, + { + "name": "debouncer", + "writable": true + }, + { + "name": "debouncer_ata", + "writable": true + } + ], + "args": [] + }, + { + "name": "create_donation_fee_pda", + "discriminator": [ + 244, + 139, + 16, + 88, + 14, + 255, + 122, + 26 + ], + "accounts": [ + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "fee_program_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "donation_fee_pda", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 100, + 111, + 110, + 97, + 116, + 105, + 111, + 110, + 45, + 102, + 101, + 101, + 45, + 112, + 100, + 97 + ] + }, + { + "kind": "account", + "path": "base_mint" + }, + { + "kind": "account", + "path": "config_id" + } + ] + } + }, + { + "name": "config_id", + "docs": [ + "stored on the PDA, so distinct `config_id`s for the same `base_mint` derive distinct addresses." + ] + }, + { + "name": "base_mint" + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pool" + }, + { + "name": "sharing_config", + "docs": [ + "(derived from `[SHARING_CONFIG_SEED, base_mint]`)" + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + } + ], + "args": [] + }, + { + "name": "create_fee_sharing_config", + "docs": [ + "Create Fee Sharing Config" + ], + "discriminator": [ + 195, + 78, + 86, + 76, + 111, + 52, + 251, + 213 + ], + "accounts": [ + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "mint" + }, + { + "name": "sharing_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "pump_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pool", + "writable": true, + "optional": true + }, + { + "name": "pump_amm_program", + "optional": true, + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "pump_amm_event_authority", + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + } + } + ], + "args": [] + }, + { + "name": "create_social_fee_pda", + "discriminator": [ + 144, + 224, + 59, + 211, + 78, + 248, + 202, + 220 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "social_fee_pda", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "fee_program_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "user_id", + "type": "string" + }, + { + "name": "platform", + "type": "u8" + } + ] + }, + { + "name": "extend_fee_config", + "docs": [ + "Realloc the fee_config PDA to [`FeeConfig::CURRENT_SIZE`] (signer pays rent delta)." + ], + "discriminator": [ + 68, + 179, + 244, + 90, + 173, + 56, + 17, + 217 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "config_program_id" + }, + { + "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": "get_fees", + "docs": [ + "Get Fees" + ], + "discriminator": [ + 231, + 37, + 126, + 85, + 207, + 91, + 63, + 52 + ], + "accounts": [ + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "config_program_id" + } + ], + "args": [ + { + "name": "is_pump_pool", + "type": "bool" + }, + { + "name": "market_cap_lamports", + "type": "u128" + }, + { + "name": "trade_size_lamports", + "type": "u64" + }, + { + "name": "is_new_quote_mint", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ], + "returns": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "get_fees_with_quote_mint", + "discriminator": [ + 154, + 237, + 138, + 92, + 162, + 2, + 162, + 187 + ], + "accounts": [ + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "config_program_id" + } + ], + "args": [ + { + "name": "is_pump_pool", + "type": "bool" + }, + { + "name": "market_cap_lamports", + "type": "u128" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ], + "returns": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "initialize_buyback", + "discriminator": [ + 250, + 129, + 236, + 160, + 227, + 36, + 103, + 134 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "buyback_vault", + "writable": true + }, + { + "name": "buyback_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_vault" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "mint" + }, + { + "name": "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": "index", + "type": "u8" + } + ] + }, + { + "name": "initialize_fee_config", + "docs": [ + "Initialize FeeConfig admin" + ], + "discriminator": [ + 62, + 162, + 20, + 133, + 121, + 65, + 145, + 27 + ], + "accounts": [ + { + "name": "admin", + "writable": true, + "signer": true, + "address": "8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8" + }, + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "config_program_id" + }, + { + "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": "initialize_fee_program_global", + "discriminator": [ + 35, + 215, + 130, + 84, + 233, + 56, + 124, + 167 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "pump_global" + ] + }, + { + "name": "pump_global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "social_claim_authority", + "type": "pubkey" + }, + { + "name": "disable_flags", + "type": "u8" + }, + { + "name": "claim_rate_limit", + "type": "u64" + } + ] + }, + { + "name": "revoke_fee_sharing_authority", + "docs": [ + "Revoke Fee Sharing Authority" + ], + "discriminator": [ + 18, + 233, + 158, + 39, + 185, + 207, + 58, + 104 + ], + "accounts": [], + "args": [] + }, + { + "name": "set_authority", + "discriminator": [ + 133, + 250, + 37, + 21, + 110, + 163, + 26, + 121 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "new_authority", + "type": "pubkey" + } + ] + }, + { + "name": "set_claim_rate_limit", + "discriminator": [ + 185, + 211, + 159, + 174, + 212, + 49, + 88, + 4 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "claim_rate_limit", + "type": "u64" + } + ] + }, + { + "name": "set_disable_flags", + "discriminator": [ + 194, + 217, + 112, + 35, + 114, + 222, + 51, + 190 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "disable_flags", + "type": "u8" + } + ] + }, + { + "name": "set_exotic_flat_fees", + "discriminator": [ + 30, + 234, + 149, + 138, + 252, + 230, + 39, + 73 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "admin", + "writable": true, + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "config_program_id" + }, + { + "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": "exotic_flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + }, + { + "name": "set_social_claim_authority", + "discriminator": [ + 147, + 54, + 184, + 154, + 136, + 237, + 185, + 153 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "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": "social_claim_authority", + "type": "pubkey" + } + ] + }, + { + "name": "sweep_buyback", + "discriminator": [ + 138, + 33, + 204, + 38, + 207, + 161, + 159, + 226 + ], + "accounts": [ + { + "name": "destination", + "writable": true + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "buyback_vault" + ] + }, + { + "name": "buyback_vault", + "writable": true + }, + { + "name": "buyback_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_vault" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "destination_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "destination" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "mint" + }, + { + "name": "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": "index", + "type": "u8" + } + ] + }, + { + "name": "transfer_fee_sharing_authority", + "docs": [ + "Transfer Fee Sharing Authority" + ], + "discriminator": [ + 202, + 10, + 75, + 200, + 164, + 34, + 210, + 96 + ], + "accounts": [], + "args": [] + }, + { + "name": "update_admin", + "docs": [ + "Update admin (only callable by admin)" + ], + "discriminator": [ + 161, + 176, + 40, + 213, + 60, + 184, + 179, + 228 + ], + "accounts": [ + { + "name": "admin", + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "new_admin" + }, + { + "name": "config_program_id" + }, + { + "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": "update_buyback_authority", + "discriminator": [ + 66, + 98, + 113, + 202, + 121, + 37, + 219, + 107 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "buyback_vault", + "writable": true + }, + { + "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": "index", + "type": "u8" + }, + { + "name": "new_authority", + "type": "pubkey" + } + ] + }, + { + "name": "update_buyback_claim_rate_limit", + "discriminator": [ + 186, + 95, + 135, + 190, + 255, + 199, + 137, + 170 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "fee_program_global" + ] + }, + { + "name": "fee_program_global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 45, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 45, + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "buyback_vault", + "writable": true + }, + { + "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": "index", + "type": "u8" + }, + { + "name": "claim_rate_limit", + "type": "i64" + } + ] + }, + { + "name": "update_fee_config", + "docs": [ + "Set/Replace fee parameters entirely (only callable by admin)" + ], + "discriminator": [ + 104, + 184, + 103, + 242, + 88, + 151, + 107, + 20 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "admin", + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "config_program_id" + }, + { + "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": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + }, + { + "name": "update_fee_shares", + "docs": [ + "Update Fee Shares, make sure to distribute all the fees before calling this" + ], + "discriminator": [ + 189, + 13, + 136, + 99, + 187, + 164, + 237, + 35 + ], + "accounts": [ + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "authority", + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "sharing_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "sharing_config" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "pump_program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "pump_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_amm_program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "amm_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + } + }, + { + "name": "wsol_mint", + "address": "So11111111111111111111111111111111111111112" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "sharing_config" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true + } + ], + "args": [ + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + }, + { + "name": "update_fee_shares_v2", + "docs": [ + "Update Fee Shares, make sure to distribute all the fees before calling this" + ], + "discriminator": [ + 111, + 251, + 49, + 6, + 78, + 78, + 106, + 18 + ], + "accounts": [ + { + "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", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "sharing_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "sharing_config" + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pump_creator_vault" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "pump_program", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "pump_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + } + }, + { + "name": "pump_amm_program", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "amm_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + } + }, + { + "name": "quote_mint" + }, + { + "name": "token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "sharing_config" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 20, + 222, + 252, + 130, + 94, + 198, + 118, + 148, + 37, + 8, + 24, + 187, + 101, + 64, + 101, + 244, + 41, + 141, + 49, + 86, + 213, + 113, + 180, + 212, + 248, + 9, + 12, + 24, + 233, + 168, + 99 + ] + } + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + } + ], + "args": [ + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + }, + { + "name": "update_stable_fee_config", + "docs": [ + "Set/Replace fee parameters entirely (only callable by admin)" + ], + "discriminator": [ + 107, + 169, + 100, + 179, + 134, + 155, + 146, + 221 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "admin", + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "config_program_id" + }, + { + "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": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + } + ] + }, + { + "name": "upsert_fee_tiers", + "docs": [ + "Update or expand fee tiers (only callable by admin)" + ], + "discriminator": [ + 227, + 23, + 150, + 12, + 77, + 86, + 94, + 4 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "admin", + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "config_program_id" + }, + { + "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": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "offset", + "type": "u8" + } + ] + }, + { + "name": "upsert_stable_fee_tiers", + "docs": [ + "Update or expand fee tiers (only callable by admin)" + ], + "discriminator": [ + 181, + 160, + 162, + 252, + 74, + 76, + 224, + 221 + ], + "accounts": [ + { + "name": "fee_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "config_program_id" + } + ] + } + }, + { + "name": "admin", + "signer": true, + "relations": [ + "fee_config" + ] + }, + { + "name": "config_program_id" + }, + { + "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": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "offset", + "type": "u8" + } + ] + } + ], + "accounts": [ + { + "name": "BondingCurve", + "discriminator": [ + 23, + 183, + 248, + 55, + 96, + 216, + 172, + 96 + ] + }, + { + "name": "BuybackVault", + "discriminator": [ + 153, + 166, + 71, + 144, + 179, + 189, + 137, + 251 + ] + }, + { + "name": "DonationFeePda", + "discriminator": [ + 246, + 197, + 96, + 9, + 193, + 30, + 93, + 115 + ] + }, + { + "name": "FeeConfig", + "discriminator": [ + 143, + 52, + 146, + 187, + 219, + 123, + 76, + 155 + ] + }, + { + "name": "FeeProgramGlobal", + "discriminator": [ + 162, + 165, + 245, + 49, + 29, + 37, + 55, + 242 + ] + }, + { + "name": "Global", + "discriminator": [ + 167, + 232, + 232, + 177, + 200, + 108, + 114, + 127 + ] + }, + { + "name": "Pool", + "discriminator": [ + 241, + 154, + 109, + 4, + 17, + 177, + 109, + 188 + ] + }, + { + "name": "SharingConfig", + "discriminator": [ + 216, + 74, + 9, + 0, + 56, + 140, + 93, + 75 + ] + }, + { + "name": "SocialFeePda", + "discriminator": [ + 139, + 96, + 53, + 17, + 42, + 169, + 206, + 150 + ] + } + ], + "events": [ + { + "name": "CreateFeeSharingConfigEvent", + "discriminator": [ + 133, + 105, + 170, + 200, + 184, + 116, + 251, + 88 + ] + }, + { + "name": "DonationFeePdaCranked", + "discriminator": [ + 30, + 208, + 107, + 93, + 177, + 0, + 223, + 78 + ] + }, + { + "name": "DonationFeePdaCreated", + "discriminator": [ + 94, + 20, + 137, + 239, + 35, + 77, + 225, + 235 + ] + }, + { + "name": "ExtendFeeConfigEvent", + "discriminator": [ + 226, + 203, + 224, + 35, + 153, + 10, + 88, + 51 + ] + }, + { + "name": "InitializeFeeConfigEvent", + "discriminator": [ + 89, + 138, + 244, + 230, + 10, + 56, + 226, + 126 + ] + }, + { + "name": "InitializeFeeProgramGlobalEvent", + "discriminator": [ + 40, + 233, + 156, + 78, + 95, + 0, + 8, + 199 + ] + }, + { + "name": "ResetFeeSharingConfigEvent", + "discriminator": [ + 203, + 204, + 151, + 226, + 120, + 55, + 214, + 243 + ] + }, + { + "name": "SetAuthorityEvent", + "discriminator": [ + 18, + 175, + 132, + 66, + 208, + 201, + 87, + 242 + ] + }, + { + "name": "SetClaimRateLimitEvent", + "discriminator": [ + 13, + 143, + 143, + 235, + 181, + 19, + 51, + 40 + ] + }, + { + "name": "SetDisableFlagsEvent", + "discriminator": [ + 5, + 8, + 179, + 65, + 49, + 55, + 145, + 126 + ] + }, + { + "name": "SetExoticFlatFeesEvent", + "discriminator": [ + 195, + 138, + 108, + 15, + 8, + 77, + 68, + 9 + ] + }, + { + "name": "SetSocialClaimAuthorityEvent", + "discriminator": [ + 60, + 118, + 127, + 132, + 239, + 52, + 254, + 14 + ] + }, + { + "name": "SocialFeePdaClaimed", + "discriminator": [ + 50, + 18, + 193, + 65, + 237, + 210, + 234, + 236 + ] + }, + { + "name": "SocialFeePdaCreated", + "discriminator": [ + 183, + 183, + 218, + 147, + 24, + 124, + 137, + 169 + ] + }, + { + "name": "SweepBuybackEvent", + "discriminator": [ + 43, + 56, + 42, + 214, + 153, + 57, + 166, + 137 + ] + }, + { + "name": "UpdateAdminEvent", + "discriminator": [ + 225, + 152, + 171, + 87, + 246, + 63, + 66, + 234 + ] + }, + { + "name": "UpdateFeeConfigEvent", + "discriminator": [ + 90, + 23, + 65, + 35, + 62, + 244, + 188, + 208 + ] + }, + { + "name": "UpdateFeeSharesEvent", + "discriminator": [ + 21, + 186, + 196, + 184, + 91, + 228, + 225, + 203 + ] + }, + { + "name": "UpdateStableFeeConfigEvent", + "discriminator": [ + 94, + 5, + 43, + 237, + 103, + 147, + 232, + 245 + ] + }, + { + "name": "UpsertFeeTiersEvent", + "discriminator": [ + 171, + 89, + 169, + 187, + 122, + 186, + 33, + 204 + ] + }, + { + "name": "UpsertStableFeeTiersEvent", + "discriminator": [ + 232, + 237, + 237, + 52, + 98, + 146, + 73, + 243 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "UnauthorizedProgram", + "msg": "Only Pump and PumpSwap programs can call this instruction" + }, + { + "code": 6001, + "name": "InvalidAdmin", + "msg": "Invalid admin" + }, + { + "code": 6002, + "name": "NoFeeTiers", + "msg": "No fee tiers provided" + }, + { + "code": 6003, + "name": "TooManyFeeTiers", + "msg": "format" + }, + { + "code": 6004, + "name": "OffsetNotContinuous", + "msg": "The offset should be <= fee_config.fee_tiers.len()" + }, + { + "code": 6005, + "name": "FeeTiersNotSorted", + "msg": "Fee tiers must be sorted by market cap threshold (ascending)" + }, + { + "code": 6006, + "name": "InvalidFeeTotal", + "msg": "Fee total must not exceed 10_000bps" + }, + { + "code": 6007, + "name": "InvalidSharingConfig", + "msg": "Invalid Sharing Config" + }, + { + "code": 6008, + "name": "InvalidPool", + "msg": "Invalid Pool" + }, + { + "code": 6009, + "name": "SharingConfigAdminRevoked", + "msg": "Sharing config authority has been revoked - sharing config can only be updated once" + }, + { + "code": 6010, + "name": "NoShareholders", + "msg": "No shareholders provided" + }, + { + "code": 6011, + "name": "TooManyShareholders", + "msg": "format" + }, + { + "code": 6012, + "name": "DuplicateShareholder", + "msg": "Duplicate shareholder address" + }, + { + "code": 6013, + "name": "NotEnoughRemainingAccounts", + "msg": "Not enough remaining accounts" + }, + { + "code": 6014, + "name": "InvalidShareTotal", + "msg": "Invalid share total - must equal 10_000 basis points" + }, + { + "code": 6015, + "name": "ShareCalculationOverflow", + "msg": "Share calculation overflow" + }, + { + "code": 6016, + "name": "NotAuthorized", + "msg": "The given account is not authorized to execute this instruction." + }, + { + "code": 6017, + "name": "ZeroShareNotAllowed", + "msg": "Shareholder cannot have zero share" + }, + { + "code": 6018, + "name": "SharingConfigNotActive", + "msg": "Fee sharing config is not active" + }, + { + "code": 6019, + "name": "AmmAccountsRequiredForGraduatedCoin", + "msg": "AMM accounts are required for graduated coins" + }, + { + "code": 6020, + "name": "ShareholderAccountMismatch", + "msg": "Remaining account key doesn't match shareholder address" + }, + { + "code": 6021, + "name": "FeatureDeactivated", + "msg": "Feature is currently deactivated" + }, + { + "code": 6022, + "name": "UserIdTooLong", + "msg": "User ID exceeds maximum length" + }, + { + "code": 6023, + "name": "DeprecatedInstruction", + "msg": "Instruction is deprecated" + }, + { + "code": 6024, + "name": "FeeSharesAlreadyUpdated", + "msg": "Reward split can only be updated once" + }, + { + "code": 6025, + "name": "MathOverflow", + "msg": "Math overflow" + }, + { + "code": 6026, + "name": "InvalidBuybackIndex", + "msg": "Invalid buybackindex" + }, + { + "code": 6027, + "name": "ClaimRateLimitExceeded", + "msg": "Claim rate limit exceeded" + }, + { + "code": 6028, + "name": "InvalidFeeConfigAccount", + "msg": "Account is not a valid FeeConfig for this instruction" + }, + { + "code": 6029, + "name": "AccountTypeNotSupported", + "msg": "Account type not supported" + }, + { + "code": 6030, + "name": "InvalidMint", + "msg": "Mint does not match quote mint" + }, + { + "code": 6031, + "name": "UnsupportedQuoteMint", + "msg": "Unsupported quote mint" + }, + { + "code": 6032, + "name": "InvalidRemainingAccounts", + "msg": "Invalid remaining accounts" + } + ], + "types": [ + { + "name": "BondingCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_quote_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + } + ] + } + }, + { + "name": "BuybackVault", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "total_claimed", + "type": "u64" + }, + { + "name": "total_claimed_token1", + "type": "u64" + }, + { + "name": "total_claimed_token2", + "type": "u64" + }, + { + "name": "last_claimed", + "type": "i64" + }, + { + "name": "claim_rate_limit", + "type": "i64" + }, + { + "name": "_reserved", + "type": { + "array": [ + "u8", + 128 + ] + } + } + ] + } + }, + { + "name": "ConfigStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Paused" + }, + { + "name": "Active" + } + ] + } + }, + { + "name": "CreateFeeSharingConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "pool", + "type": { + "option": "pubkey" + } + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "initial_shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "status", + "type": { + "defined": { + "name": "ConfigStatus" + } + } + } + ] + } + }, + { + "name": "DonationFeePda", + "docs": [ + "Escrow PDA for donation relay: one per (mint, donation campaign `config_id`)." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "config_id", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "total_donated", + "type": "u64" + }, + { + "name": "last_crank_ts", + "type": "i64" + }, + { + "name": "_reserved", + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "DonationFeePdaCranked", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "donation_fee_pda", + "type": "pubkey" + }, + { + "name": "config_id", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "DonationFeePdaCreated", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "created_by", + "type": "pubkey" + }, + { + "name": "donation_fee_pda", + "type": "pubkey" + }, + { + "name": "config_id", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "ExtendFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "current_size", + "type": "u64" + }, + { + "name": "new_size", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "FeeConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "The bump for the PDA" + ], + "type": "u8" + }, + { + "name": "admin", + "docs": [ + "The admin account that can update the fee config" + ], + "type": "pubkey" + }, + { + "name": "flat_fees", + "docs": [ + "The flat fees for non-pump pools" + ], + "type": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "fee_tiers", + "docs": [ + "The fee tiers" + ], + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "stable_fee_tiers", + "docs": [ + "The fee tiers" + ], + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "exotic_flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "FeeProgramGlobal", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "disable_flags", + "type": "u8" + }, + { + "name": "social_claim_authority", + "type": "pubkey" + }, + { + "name": "claim_rate_limit", + "type": "u64" + }, + { + "name": "_reserved", + "type": { + "array": [ + "u8", + 256 + ] + } + } + ] + } + }, + { + "name": "FeeTier", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market_cap_lamports_threshold", + "type": "u128" + }, + { + "name": "fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "Fees", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lp_fee_bps", + "type": "u64" + }, + { + "name": "protocol_fee_bps", + "type": "u64" + }, + { + "name": "creator_fee_bps", + "type": "u64" + } + ] + } + }, + { + "name": "Global", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "type": "bool" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + }, + { + "name": "create_v2_enabled", + "type": "bool" + }, + { + "name": "whitelist_pda", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "mayhem_mode_enabled", + "type": "bool" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + } + ] + } + }, + { + "name": "InitializeFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + } + ] + } + }, + { + "name": "InitializeFeeProgramGlobalEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "social_claim_authority", + "type": "pubkey" + }, + { + "name": "disable_flags", + "type": "u8" + }, + { + "name": "claim_rate_limit", + "type": "u64" + } + ] + } + }, + { + "name": "OptionBool", + "type": { + "kind": "struct", + "fields": [ + "bool" + ] + } + }, + { + "name": "Pool", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pool_bump", + "type": "u8" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "base_mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "lp_mint", + "type": "pubkey" + }, + { + "name": "pool_base_token_account", + "type": "pubkey" + }, + { + "name": "pool_quote_token_account", + "type": "pubkey" + }, + { + "name": "lp_supply", + "type": "u64" + }, + { + "name": "coin_creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "virtual_quote_reserves", + "type": "i128" + } + ] + } + }, + { + "name": "ResetFeeSharingConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "old_admin", + "type": "pubkey" + }, + { + "name": "old_shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "new_admin", + "type": "pubkey" + }, + { + "name": "new_shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "old_version", + "type": "u8" + }, + { + "name": "new_version", + "type": "u8" + } + ] + } + }, + { + "name": "SetAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "old_authority", + "type": "pubkey" + }, + { + "name": "new_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetClaimRateLimitEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "claim_rate_limit", + "type": "u64" + } + ] + } + }, + { + "name": "SetDisableFlagsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "disable_flags", + "type": "u8" + } + ] + } + }, + { + "name": "SetExoticFlatFeesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "exotic_flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "SetSocialClaimAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "social_claim_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "Shareholder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "pubkey" + }, + { + "name": "share_bps", + "type": "u16" + } + ] + } + }, + { + "name": "SharingConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "status", + "type": { + "defined": { + "name": "ConfigStatus" + } + } + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "admin_revoked", + "type": "bool" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + } + }, + { + "name": "SocialFeePda", + "docs": [ + "Platform identifier: 0=pump, 1=twitter, etc." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "user_id", + "docs": [ + "Max 20 characters to fit u64::MAX (18,446,744,073,709,551,615) as a string.", + "Actual storage: 4 bytes (length prefix) + 20 bytes (content) = 24 bytes." + ], + "type": "string" + }, + { + "name": "platform", + "type": "u8" + }, + { + "name": "total_claimed", + "type": "u64" + }, + { + "name": "last_claimed", + "type": "u64" + }, + { + "name": "total_stable_claimed", + "type": "u64" + }, + { + "name": "_reserved", + "type": { + "array": [ + "u8", + 120 + ] + } + } + ] + } + }, + { + "name": "SocialFeePdaClaimed", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "user_id", + "type": "string" + }, + { + "name": "platform", + "type": "u8" + }, + { + "name": "social_fee_pda", + "type": "pubkey" + }, + { + "name": "recipient", + "type": "pubkey" + }, + { + "name": "social_claim_authority", + "type": "pubkey" + }, + { + "name": "amount_claimed", + "type": "u64" + }, + { + "name": "claimable_before", + "type": "u64" + }, + { + "name": "lifetime_claimed", + "type": "u64" + }, + { + "name": "recipient_balance_before", + "type": "u64" + }, + { + "name": "recipient_balance_after", + "type": "u64" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "lifetime_stable_claimed", + "type": "u64" + } + ] + } + }, + { + "name": "SocialFeePdaCreated", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "user_id", + "type": "string" + }, + { + "name": "platform", + "type": "u8" + }, + { + "name": "social_fee_pda", + "type": "pubkey" + }, + { + "name": "created_by", + "type": "pubkey" + } + ] + } + }, + { + "name": "SweepBuybackEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "index", + "type": "u8" + }, + { + "name": "sol_amount", + "type": "u64" + }, + { + "name": "token_amount", + "type": "u64" + }, + { + "name": "destination", + "type": "pubkey" + }, + { + "name": "buyback_vault", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "UpdateAdminEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "old_admin", + "type": "pubkey" + }, + { + "name": "new_admin", + "type": "pubkey" + } + ] + } + }, + { + "name": "UpdateFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "UpdateFeeSharesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "new_shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "version", + "type": "u8" + } + ] + } + }, + { + "name": "UpdateStableFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "UpsertFeeTiersEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "offset", + "type": "u8" + } + ] + } + }, + { + "name": "UpsertStableFeeTiersEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "fee_config", + "type": "pubkey" + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "offset", + "type": "u8" + } + ] + } + } + ], + "constants": [ + { + "name": "AMM_CREATOR_VAULT_AUTHORITY_SEED", + "type": { + "array": [ + "u8", + 13 + ] + }, + "value": "[99, 114, 101, 97, 116, 111, 114, 95, 118, 97, 117, 108, 116]" + }, + { + "name": "BUYBACK_VAULT_SEED", + "type": { + "array": [ + "u8", + 13 + ] + }, + "value": "[98, 117, 121, 98, 97, 99, 107, 45, 118, 97, 117, 108, 116]" + }, + { + "name": "DEBOUNCER_V1", + "type": "bytes", + "value": "[100, 101, 98, 111, 117, 110, 99, 101, 114, 95, 118, 49]" + }, + { + "name": "DONATION_FEE_PDA_SEED", + "type": { + "array": [ + "u8", + 16 + ] + }, + "value": "[100, 111, 110, 97, 116, 105, 111, 110, 45, 102, 101, 101, 45, 112, 100, 97]" + }, + { + "name": "EPOCH_TRACKER_V1", + "type": "bytes", + "value": "[101, 112, 111, 99, 104, 95, 116, 114, 97, 99, 107, 101, 114, 95, 118, 49]" + }, + { + "name": "FEE_CONFIG_SEED", + "type": "bytes", + "value": "[102, 101, 101, 95, 99, 111, 110, 102, 105, 103]" + }, + { + "name": "FEE_PROGRAM_GLOBAL_SEED", + "type": { + "array": [ + "u8", + 18 + ] + }, + "value": "[102, 101, 101, 45, 112, 114, 111, 103, 114, 97, 109, 45, 103, 108, 111, 98, 97, 108]" + }, + { + "name": "IX_DONATE_PUBKEY_CONFIG_ID_WITH_PAYER_V1", + "type": { + "array": [ + "u8", + 8 + ] + }, + "value": "[120, 217, 57, 241, 135, 104, 139, 184]" + }, + { + "name": "MAX_BUYBACK_INDEX", + "type": "u8", + "value": "8" + }, + { + "name": "MINT_WHITELIST_V1", + "type": "bytes", + "value": "[109, 105, 110, 116, 95, 119, 104, 105, 116, 101, 108, 105, 115, 116, 95, 118, 49]" + }, + { + "name": "PUMP_CREATOR_VAULT_SEED", + "type": { + "array": [ + "u8", + 13 + ] + }, + "value": "[99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116]" + }, + { + "name": "PUMP_GLOBAL_SEED", + "docs": [ + "Bonding Curve Program Global Seed" + ], + "type": { + "array": [ + "u8", + 6 + ] + }, + "value": "[103, 108, 111, 98, 97, 108]" + }, + { + "name": "SHARING_CONFIG_SEED", + "type": { + "array": [ + "u8", + 14 + ] + }, + "value": "[115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, 103]" + }, + { + "name": "SOCIAL_FEE_PDA_SEED", + "type": { + "array": [ + "u8", + 14 + ] + }, + "value": "[115, 111, 99, 105, 97, 108, 45, 102, 101, 101, 45, 112, 100, 97]" + } + ] +} \ No newline at end of file diff --git a/idl/pump_fun_idl.json b/idl/pump_fun_idl.json index 7c7f836..2f1c65d 100644 --- a/idl/pump_fun_idl.json +++ b/idl/pump_fun_idl.json @@ -1,7098 +1,12387 @@ -{ - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", - "metadata": { - "name": "pump", - "version": "0.1.0", - "spec": "0.1.0", - "description": "Created with Anchor" - }, - "instructions": [ - { - "name": "admin_set_creator", - "docs": [ - "Allows Global::admin_set_creator_authority to override the bonding curve creator" - ], - "discriminator": [ - 69, - 25, - 171, - 142, - 57, - 239, - 13, - 4 - ], - "accounts": [ - { - "name": "admin_set_creator_authority", - "signer": true, - "relations": [ - "global" - ] - }, - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "mint" - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "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": "creator", - "type": "pubkey" - } - ] - }, - { - "name": "admin_set_idl_authority", - "discriminator": [ - 8, - 217, - 96, - 231, - 144, - 104, - 192, - 5 - ], - "accounts": [ - { - "name": "authority", - "signer": true, - "relations": [ - "global" - ] - }, - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "idl_account", - "writable": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "program_signer", - "pda": { - "seeds": [] - } - }, - { - "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": "idl_authority", - "type": "pubkey" - } - ] - }, - { - "name": "admin_update_token_incentives", - "discriminator": [ - 209, - 11, - 115, - 87, - 213, - 23, - 124, - 204 - ], - "accounts": [ - { - "name": "authority", - "writable": true, - "signer": true, - "relations": [ - "global" - ] - }, - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "global_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - } - ] - } - }, - { - "name": "mint" - }, - { - "name": "global_incentive_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "global_volume_accumulator" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "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": "start_time", - "type": "i64" - }, - { - "name": "end_time", - "type": "i64" - }, - { - "name": "seconds_in_a_day", - "type": "i64" - }, - { - "name": "day_number", - "type": "u64" - }, - { - "name": "pump_token_supply_per_day", - "type": "u64" - } - ] - }, - { - "name": "buy", - "docs": [ - "Buys tokens from a bonding curve." - ], - "discriminator": [ - 102, - 6, - 61, - 18, - 1, - 218, - 235, - 234 - ], - "accounts": [ - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "fee_recipient", - "writable": true - }, - { - "name": "mint" - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "associated_user", - "writable": true - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program" - }, - { - "name": "creator_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "bonding_curve.creator", - "account": "BondingCurve" - } - ] - } - }, - { - "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - }, - { - "name": "global_volume_accumulator", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - } - ] - } - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "name": "fee_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 102, - 101, - 101, - 95, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "const", - "value": [ - 1, - 86, - 224, - 246, - 147, - 102, - 90, - 207, - 68, - 219, - 21, - 104, - 191, - 23, - 91, - 170, - 81, - 137, - 203, - 151, - 245, - 210, - 255, - 59, - 101, - 93, - 43, - 182, - 253, - 109, - 24, - 176 - ] - } - ], - "program": { - "kind": "account", - "path": "fee_program" - } - } - }, - { - "name": "fee_program", - "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "max_sol_cost", - "type": "u64" - }, - { - "name": "track_volume", - "type": { - "defined": { - "name": "OptionBool" - } - } - } - ] - }, - { - "name": "buy_exact_sol_in", - "docs": [ - "Given a budget of spendable SOL, buy at least min_tokens_out tokens.", - "Fees are deducted from spendable_sol_in.", - "", - "# Quote formulas", - "Where:", - "- total_fee_bps = protocol_fee_bps + creator_fee_bps (creator_fee_bps is 0 if no creator)", - "- floor(a/b) = a / b (integer division)", - "- ceil(a/b) = (a + b - 1) / b", - "", - "SOL → tokens quote", - "To calculate tokens_out for a given spendable_sol_in:", - "1. net_sol = floor(spendable_sol_in * 10_000 / (10_000 + total_fee_bps))", - "2. fees = ceil(net_sol * protocol_fee_bps / 10_000) + ceil(net_sol * creator_fee_bps / 10_000) (creator_fee_bps is 0 if no creator)", - "3. if net_sol + fees > spendable_sol_in: net_sol = net_sol - (net_sol + fees - spendable_sol_in)", - "4. tokens_out = floor((net_sol - 1) * virtual_token_reserves / (virtual_sol_reserves + net_sol - 1))", - "", - "Reverse quote (tokens → SOL)", - "To calculate spendable_sol_in for a desired number of tokens:", - "1. net_sol = ceil(tokens * virtual_sol_reserves / (virtual_token_reserves - tokens)) + 1", - "2. spendable_sol_in = ceil(net_sol * (10_000 + total_fee_bps) / 10_000)", - "", - "Rent", - "Separately make sure the instruction's payer has enough SOL to cover rent for:", - "- creator_vault: rent.minimum_balance(0)", - "- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)" - ], - "discriminator": [ - 56, - 252, - 116, - 8, - 158, - 223, - 205, - 95 - ], - "accounts": [ - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "fee_recipient", - "writable": true - }, - { - "name": "mint" - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "associated_user", - "writable": true - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program" - }, - { - "name": "creator_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "bonding_curve.creator", - "account": "BondingCurve" - } - ] - } - }, - { - "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - }, - { - "name": "global_volume_accumulator", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - } - ] - } - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "name": "fee_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 102, - 101, - 101, - 95, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "const", - "value": [ - 1, - 86, - 224, - 246, - 147, - 102, - 90, - 207, - 68, - 219, - 21, - 104, - 191, - 23, - 91, - 170, - 81, - 137, - 203, - 151, - 245, - 210, - 255, - 59, - 101, - 93, - 43, - 182, - 253, - 109, - 24, - 176 - ] - } - ], - "program": { - "kind": "account", - "path": "fee_program" - } - } - }, - { - "name": "fee_program", - "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" - } - ], - "args": [ - { - "name": "spendable_sol_in", - "type": "u64" - }, - { - "name": "min_tokens_out", - "type": "u64" - }, - { - "name": "track_volume", - "type": { - "defined": { - "name": "OptionBool" - } - } - } - ] - }, - { - "name": "claim_cashback", - "discriminator": [ - 37, - 58, - 35, - 126, - 190, - 53, - 228, - 197 - ], - "accounts": [ - { - "name": "user", - "writable": true - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [] - }, - { - "name": "claim_token_incentives", - "discriminator": [ - 16, - 4, - 71, - 28, - 204, - 1, - 40, - 27 - ], - "accounts": [ - { - "name": "user" - }, - { - "name": "user_ata", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "user" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "global_volume_accumulator", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - } - ] - } - }, - { - "name": "global_incentive_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "global_volume_accumulator" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "name": "mint", - "relations": [ - "global_volume_accumulator" - ] - }, - { - "name": "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - }, - { - "name": "payer", - "writable": true, - "signer": true - } - ], - "args": [] - }, - { - "name": "close_user_volume_accumulator", - "discriminator": [ - 249, - 69, - 164, - 218, - 150, - 103, - 84, - 138 - ], - "accounts": [ - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "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": "collect_creator_fee", - "docs": [ - "Collects creator_fee from creator_vault to the coin creator account" - ], - "discriminator": [ - 20, - 22, - 86, - 123, - 198, - 28, - 219, - 132 - ], - "accounts": [ - { - "name": "creator", - "writable": true - }, - { - "name": "creator_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "creator" - } - ] - } - }, - { - "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": "create", - "docs": [ - "Creates a new coin and bonding curve." - ], - "discriminator": [ - 24, - 30, - 200, - 40, - 5, - 28, - 7, - 119 - ], - "accounts": [ - { - "name": "mint", - "writable": true, - "signer": true - }, - { - "name": "mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 105, - 110, - 116, - 45, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] - } - ] - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "const", - "value": [ - 6, - 221, - 246, - 225, - 215, - 101, - 161, - 147, - 217, - 203, - 225, - 70, - 206, - 235, - 121, - 172, - 28, - 180, - 133, - 237, - 95, - 91, - 55, - 145, - 58, - 140, - 245, - 133, - 126, - 255, - 0, - 169 - ] - }, - { - "kind": "account", - "path": "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": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "mpl_token_metadata", - "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" - }, - { - "name": "metadata", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 101, - 116, - 97, - 100, - 97, - 116, - 97 - ] - }, - { - "kind": "const", - "value": [ - 11, - 112, - 101, - 177, - 227, - 209, - 124, - 69, - 56, - 157, - 82, - 127, - 107, - 4, - 195, - 205, - 88, - 184, - 108, - 115, - 26, - 160, - 253, - 181, - 73, - 182, - 209, - 188, - 3, - 248, - 41, - 70 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "account", - "path": "mpl_token_metadata" - } - } - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "rent", - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "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": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, - { - "name": "uri", - "type": "string" - }, - { - "name": "creator", - "type": "pubkey" - } - ] - }, - { - "name": "create_v2", - "docs": [ - "Creates a new spl-22 coin and bonding curve." - ], - "discriminator": [ - 214, - 144, - 76, - 236, - 95, - 139, - 49, - 180 - ], - "accounts": [ - { - "name": "mint", - "writable": true, - "signer": true - }, - { - "name": "mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 105, - 110, - 116, - 45, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] - } - ] - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "mayhem_program_id", - "writable": true, - "address": "MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e" - }, - { - "name": "global_params", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 45, - 112, - 97, - 114, - 97, - 109, - 115 - ] - } - ], - "program": { - "kind": "const", - "value": [ - 5, - 42, - 229, - 215, - 167, - 218, - 167, - 36, - 166, - 234, - 176, - 167, - 41, - 84, - 145, - 133, - 90, - 212, - 160, - 103, - 22, - 96, - 103, - 76, - 78, - 3, - 69, - 89, - 128, - 61, - 101, - 163 - ] - } - } - }, - { - "name": "sol_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 115, - 111, - 108, - 45, - 118, - 97, - 117, - 108, - 116 - ] - } - ], - "program": { - "kind": "const", - "value": [ - 5, - 42, - 229, - 215, - 167, - 218, - 167, - 36, - 166, - 234, - 176, - 167, - 41, - 84, - 145, - 133, - 90, - 212, - 160, - 103, - 22, - 96, - 103, - 76, - 78, - 3, - 69, - 89, - 128, - 61, - 101, - 163 - ] - } - } - }, - { - "name": "mayhem_state", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 97, - 121, - 104, - 101, - 109, - 45, - 115, - 116, - 97, - 116, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 5, - 42, - 229, - 215, - 167, - 218, - 167, - 36, - 166, - 234, - 176, - 167, - 41, - 84, - 145, - 133, - 90, - 212, - 160, - 103, - 22, - 96, - 103, - 76, - 78, - 3, - 69, - 89, - 128, - 61, - 101, - 163 - ] - } - } - }, - { - "name": "mayhem_token_vault", - "writable": true - }, - { - "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": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, - { - "name": "uri", - "type": "string" - }, - { - "name": "creator", - "type": "pubkey" - }, - { - "name": "is_mayhem_mode", - "type": "bool" - }, - { - "name": "is_cashback_enabled", - "type": { - "defined": { - "name": "OptionBool" - } - } - } - ] - }, - { - "name": "distribute_creator_fees", - "docs": [ - "Distributes creator fees to shareholders based on their share percentages", - "The creator vault needs to have at least the minimum distributable amount to distribute fees", - "This can be checked with the get_minimum_distributable_fee instruction" - ], - "discriminator": [ - 165, - 114, - 103, - 0, - 121, - 206, - 247, - 81 - ], - "accounts": [ - { - "name": "mint", - "relations": [ - "sharing_config" - ] - }, - { - "name": "bonding_curve", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "sharing_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 115, - 104, - 97, - 114, - 105, - 110, - 103, - 45, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 12, - 53, - 255, - 169, - 5, - 90, - 142, - 86, - 141, - 168, - 247, - 188, - 7, - 86, - 21, - 39, - 76, - 241, - 201, - 44, - 164, - 31, - 64, - 0, - 156, - 81, - 106, - 164, - 20, - 194, - 124, - 112 - ] - } - } - }, - { - "name": "creator_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "bonding_curve.creator", - "account": "BondingCurve" - } - ] - } - }, - { - "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [], - "returns": { - "defined": { - "name": "DistributeCreatorFeesEvent" - } - } - }, - { - "name": "extend_account", - "docs": [ - "Extends the size of program-owned accounts" - ], - "discriminator": [ - 234, - 102, - 194, - 203, - 150, - 72, - 62, - 229 - ], - "accounts": [ - { - "name": "account", - "writable": true - }, - { - "name": "user", - "signer": true - }, - { - "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": "get_minimum_distributable_fee", - "docs": [ - "Permissionless instruction to check the minimum required fees for distribution", - "Returns the minimum required balance from the creator_vault and whether distribution can proceed" - ], - "discriminator": [ - 117, - 225, - 127, - 202, - 134, - 95, - 68, - 35 - ], - "accounts": [ - { - "name": "mint", - "relations": [ - "sharing_config" - ] - }, - { - "name": "bonding_curve", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "sharing_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 115, - 104, - 97, - 114, - 105, - 110, - 103, - 45, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 12, - 53, - 255, - 169, - 5, - 90, - 142, - 86, - 141, - 168, - 247, - 188, - 7, - 86, - 21, - 39, - 76, - 241, - 201, - 44, - 164, - 31, - 64, - 0, - 156, - 81, - 106, - 164, - 20, - 194, - 124, - 112 - ] - } - } - }, - { - "name": "creator_vault", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "bonding_curve.creator", - "account": "BondingCurve" - } - ] - } - } - ], - "args": [], - "returns": { - "defined": { - "name": "MinimumDistributableFeeEvent" - } - } - }, - { - "name": "init_user_volume_accumulator", - "discriminator": [ - 94, - 6, - 202, - 115, - 255, - 96, - 232, - 183 - ], - "accounts": [ - { - "name": "payer", - "writable": true, - "signer": true - }, - { - "name": "user" - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "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": "initialize", - "docs": [ - "Creates the global state." - ], - "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [] - }, - { - "name": "migrate", - "docs": [ - "Migrates liquidity to pump_amm if the bonding curve is complete" - ], - "discriminator": [ - 155, - 234, - 231, - 146, - 236, - 158, - 162, - 30 - ], - "accounts": [ - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "withdraw_authority", - "writable": true, - "relations": [ - "global" - ] - }, - { - "name": "mint" - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "account", - "path": "mint" - }, - { - "kind": "account", - "path": "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": "user", - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "pump_amm", - "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" - }, - { - "name": "pool", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 112, - 111, - 111, - 108 - ] - }, - { - "kind": "const", - "value": [ - 0, - 0 - ] - }, - { - "kind": "account", - "path": "pool_authority" - }, - { - "kind": "account", - "path": "mint" - }, - { - "kind": "account", - "path": "wsol_mint" - } - ], - "program": { - "kind": "account", - "path": "pump_amm" - } - } - }, - { - "name": "pool_authority", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 112, - 111, - 111, - 108, - 45, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "pool_authority_mint_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "pool_authority" - }, - { - "kind": "account", - "path": "mint" - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "account", - "path": "associated_token_program" - } - } - }, - { - "name": "pool_authority_wsol_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "pool_authority" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "wsol_mint" - } - ], - "program": { - "kind": "account", - "path": "associated_token_program" - } - } - }, - { - "name": "amm_global_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 99, - 111, - 110, - 102, - 105, - 103 - ] - } - ], - "program": { - "kind": "account", - "path": "pump_amm" - } - } - }, - { - "name": "wsol_mint", - "address": "So11111111111111111111111111111111111111112" - }, - { - "name": "lp_mint", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 112, - 111, - 111, - 108, - 95, - 108, - 112, - 95, - 109, - 105, - 110, - 116 - ] - }, - { - "kind": "account", - "path": "pool" - } - ], - "program": { - "kind": "account", - "path": "pump_amm" - } - } - }, - { - "name": "user_pool_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "pool_authority" - }, - { - "kind": "account", - "path": "token_2022_program" - }, - { - "kind": "account", - "path": "lp_mint" - } - ], - "program": { - "kind": "account", - "path": "associated_token_program" - } - } - }, - { - "name": "pool_base_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "pool" - }, - { - "kind": "account", - "path": "mint" - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "account", - "path": "associated_token_program" - } - } - }, - { - "name": "pool_quote_token_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "pool" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "wsol_mint" - } - ], - "program": { - "kind": "account", - "path": "associated_token_program" - } - } - }, - { - "name": "token_2022_program", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "pump_amm_event_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 95, - 95, - 101, - 118, - 101, - 110, - 116, - 95, - 97, - 117, - 116, - 104, - 111, - 114, - 105, - 116, - 121 - ] - } - ], - "program": { - "kind": "account", - "path": "pump_amm" - } - } - }, - { - "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": "migrate_bonding_curve_creator", - "discriminator": [ - 87, - 124, - 52, - 191, - 52, - 38, - 214, - 232 - ], - "accounts": [ - { - "name": "mint", - "relations": [ - "sharing_config" - ] - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "sharing_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 115, - 104, - 97, - 114, - 105, - 110, - 103, - 45, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 12, - 53, - 255, - 169, - 5, - 90, - 142, - 86, - 141, - 168, - 247, - 188, - 7, - 86, - 21, - 39, - 76, - 241, - 201, - 44, - 164, - 31, - 64, - 0, - 156, - 81, - 106, - 164, - 20, - 194, - 124, - 112 - ] - } - } - }, - { - "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": "sell", - "docs": [ - "Sells tokens into a bonding curve.", - "For cashback coins, optionally pass user_volume_accumulator as remaining_accounts[0].", - "If provided and valid, creator_fee goes to user_volume_accumulator.", - "Otherwise, falls back to transferring creator_fee to creator_vault." - ], - "discriminator": [ - 51, - 230, - 133, - 164, - 1, - 127, - 131, - 173 - ], - "accounts": [ - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "fee_recipient", - "writable": true - }, - { - "name": "mint" - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "bonding_curve" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "associated_user", - "writable": true - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "creator_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 99, - 114, - 101, - 97, - 116, - 111, - 114, - 45, - 118, - 97, - 117, - 108, - 116 - ] - }, - { - "kind": "account", - "path": "bonding_curve.creator", - "account": "BondingCurve" - } - ] - } - }, - { - "name": "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", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - }, - { - "name": "fee_config", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 102, - 101, - 101, - 95, - 99, - 111, - 110, - 102, - 105, - 103 - ] - }, - { - "kind": "const", - "value": [ - 1, - 86, - 224, - 246, - 147, - 102, - 90, - 207, - 68, - 219, - 21, - 104, - 191, - 23, - 91, - 170, - 81, - 137, - 203, - 151, - 245, - 210, - 255, - 59, - 101, - 93, - 43, - 182, - 253, - 109, - 24, - 176 - ] - } - ], - "program": { - "kind": "account", - "path": "fee_program" - } - } - }, - { - "name": "fee_program", - "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "min_sol_output", - "type": "u64" - } - ] - }, - { - "name": "set_creator", - "docs": [ - "Allows Global::set_creator_authority to set the bonding curve creator from Metaplex metadata or input argument" - ], - "discriminator": [ - 254, - 148, - 255, - 112, - 207, - 142, - 170, - 165 - ], - "accounts": [ - { - "name": "set_creator_authority", - "signer": true, - "relations": [ - "global" - ] - }, - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "mint" - }, - { - "name": "metadata", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 101, - 116, - 97, - 100, - 97, - 116, - 97 - ] - }, - { - "kind": "const", - "value": [ - 11, - 112, - 101, - 177, - 227, - 209, - 124, - 69, - 56, - 157, - 82, - 127, - 107, - 4, - 195, - 205, - 88, - 184, - 108, - 115, - 26, - 160, - 253, - 181, - 73, - 182, - 209, - 188, - 3, - 248, - 41, - 70 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 11, - 112, - 101, - 177, - 227, - 209, - 124, - 69, - 56, - 157, - 82, - 127, - 107, - 4, - 195, - 205, - 88, - 184, - 108, - 115, - 26, - 160, - 253, - 181, - 73, - 182, - 209, - 188, - 3, - 248, - 41, - 70 - ] - } - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "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": "creator", - "type": "pubkey" - } - ] - }, - { - "name": "set_mayhem_virtual_params", - "discriminator": [ - 61, - 169, - 188, - 191, - 153, - 149, - 42, - 97 - ], - "accounts": [ - { - "name": "sol_vault_authority", - "writable": true, - "signer": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 115, - 111, - 108, - 45, - 118, - 97, - 117, - 108, - 116 - ] - } - ], - "program": { - "kind": "const", - "value": [ - 5, - 42, - 229, - 215, - 167, - 218, - 167, - 36, - 166, - 234, - 176, - 167, - 41, - 84, - 145, - 133, - 90, - 212, - 160, - 103, - 22, - 96, - 103, - 76, - 78, - 3, - 69, - 89, - 128, - 61, - 101, - 163 - ] - } - } - }, - { - "name": "mayhem_token_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "sol_vault_authority" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "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": "mint" - }, - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "token_program", - "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "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_metaplex_creator", - "docs": [ - "Syncs the bonding curve creator with the Metaplex metadata creator if it exists" - ], - "discriminator": [ - 138, - 96, - 174, - 217, - 48, - 85, - 197, - 246 - ], - "accounts": [ - { - "name": "mint" - }, - { - "name": "metadata", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, - 101, - 116, - 97, - 100, - 97, - 116, - 97 - ] - }, - { - "kind": "const", - "value": [ - 11, - 112, - 101, - 177, - 227, - 209, - 124, - 69, - 56, - 157, - 82, - 127, - 107, - 4, - 195, - 205, - 88, - 184, - 108, - 115, - 26, - 160, - 253, - 181, - 73, - 182, - 209, - 188, - 3, - 248, - 41, - 70 - ] - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 11, - 112, - 101, - 177, - 227, - 209, - 124, - 69, - 56, - 157, - 82, - 127, - 107, - 4, - 195, - 205, - 88, - 184, - 108, - 115, - 26, - 160, - 253, - 181, - 73, - 182, - 209, - 188, - 3, - 248, - 41, - 70 - ] - } - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, - 111, - 110, - 100, - 105, - 110, - 103, - 45, - 99, - 117, - 114, - 118, - 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "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_params", - "docs": [ - "Sets the global state parameters." - ], - "discriminator": [ - 27, - 234, - 178, - 52, - 147, - 2, - 187, - 141 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "writable": true, - "signer": true, - "relations": [ - "global" - ] - }, - { - "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": "initial_virtual_token_reserves", - "type": "u64" - }, - { - "name": "initial_virtual_sol_reserves", - "type": "u64" - }, - { - "name": "initial_real_token_reserves", - "type": "u64" - }, - { - "name": "token_total_supply", - "type": "u64" - }, - { - "name": "fee_basis_points", - "type": "u64" - }, - { - "name": "withdraw_authority", - "type": "pubkey" - }, - { - "name": "enable_migrate", - "type": "bool" - }, - { - "name": "pool_migration_fee", - "type": "u64" - }, - { - "name": "creator_fee_basis_points", - "type": "u64" - }, - { - "name": "set_creator_authority", - "type": "pubkey" - }, - { - "name": "admin_set_creator_authority", - "type": "pubkey" - } - ] - }, - { - "name": "set_reserved_fee_recipients", - "discriminator": [ - 111, - 172, - 162, - 232, - 114, - 89, - 213, - 142 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "signer": true, - "relations": [ - "global" - ] - }, - { - "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": "whitelist_pda", - "type": "pubkey" - } - ] - }, - { - "name": "sync_user_volume_accumulator", - "discriminator": [ - 86, - 31, - 192, - 87, - 163, - 87, - 79, - 238 - ], - "accounts": [ - { - "name": "user" - }, - { - "name": "global_volume_accumulator", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - } - ] - } - }, - { - "name": "user_volume_accumulator", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 117, - 115, - 101, - 114, - 95, - 118, - 111, - 108, - 117, - 109, - 101, - 95, - 97, - 99, - 99, - 117, - 109, - 117, - 108, - 97, - 116, - 111, - 114 - ] - }, - { - "kind": "account", - "path": "user" - } - ] - } - }, - { - "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": "toggle_cashback_enabled", - "discriminator": [ - 115, - 103, - 224, - 255, - 189, - 89, - 86, - 195 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "writable": true, - "signer": true, - "relations": [ - "global" - ] - }, - { - "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": "enabled", - "type": "bool" - } - ] - }, - { - "name": "toggle_create_v2", - "discriminator": [ - 28, - 255, - 230, - 240, - 172, - 107, - 203, - 171 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "writable": true, - "signer": true, - "relations": [ - "global" - ] - }, - { - "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": "enabled", - "type": "bool" - } - ] - }, - { - "name": "toggle_mayhem_mode", - "discriminator": [ - 1, - 9, - 111, - 208, - 100, - 31, - 255, - 163 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "writable": true, - "signer": true, - "relations": [ - "global" - ] - }, - { - "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": "enabled", - "type": "bool" - } - ] - }, - { - "name": "update_global_authority", - "discriminator": [ - 227, - 181, - 74, - 196, - 208, - 21, - 97, - 213 - ], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 103, - 108, - 111, - 98, - 97, - 108 - ] - } - ] - } - }, - { - "name": "authority", - "signer": true, - "relations": [ - "global" - ] - }, - { - "name": "new_authority" - }, - { - "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": [] - } - ], - "accounts": [ - { - "name": "BondingCurve", - "discriminator": [ - 23, - 183, - 248, - 55, - 96, - 216, - 172, - 96 - ] - }, - { - "name": "FeeConfig", - "discriminator": [ - 143, - 52, - 146, - 187, - 219, - 123, - 76, - 155 - ] - }, - { - "name": "Global", - "discriminator": [ - 167, - 232, - 232, - 177, - 200, - 108, - 114, - 127 - ] - }, - { - "name": "GlobalVolumeAccumulator", - "discriminator": [ - 202, - 42, - 246, - 43, - 142, - 190, - 30, - 255 - ] - }, - { - "name": "SharingConfig", - "discriminator": [ - 216, - 74, - 9, - 0, - 56, - 140, - 93, - 75 - ] - }, - { - "name": "UserVolumeAccumulator", - "discriminator": [ - 86, - 255, - 112, - 14, - 102, - 53, - 154, - 250 - ] - } - ], - "events": [ - { - "name": "AdminSetCreatorEvent", - "discriminator": [ - 64, - 69, - 192, - 104, - 29, - 30, - 25, - 107 - ] - }, - { - "name": "AdminSetIdlAuthorityEvent", - "discriminator": [ - 245, - 59, - 70, - 34, - 75, - 185, - 109, - 92 - ] - }, - { - "name": "AdminUpdateTokenIncentivesEvent", - "discriminator": [ - 147, - 250, - 108, - 120, - 247, - 29, - 67, - 222 - ] - }, - { - "name": "ClaimCashbackEvent", - "discriminator": [ - 226, - 214, - 246, - 33, - 7, - 242, - 147, - 229 - ] - }, - { - "name": "ClaimTokenIncentivesEvent", - "discriminator": [ - 79, - 172, - 246, - 49, - 205, - 91, - 206, - 232 - ] - }, - { - "name": "CloseUserVolumeAccumulatorEvent", - "discriminator": [ - 146, - 159, - 189, - 172, - 146, - 88, - 56, - 244 - ] - }, - { - "name": "CollectCreatorFeeEvent", - "discriminator": [ - 122, - 2, - 127, - 1, - 14, - 191, - 12, - 175 - ] - }, - { - "name": "CompleteEvent", - "discriminator": [ - 95, - 114, - 97, - 156, - 212, - 46, - 152, - 8 - ] - }, - { - "name": "CompletePumpAmmMigrationEvent", - "discriminator": [ - 189, - 233, - 93, - 185, - 92, - 148, - 234, - 148 - ] - }, - { - "name": "CreateEvent", - "discriminator": [ - 27, - 114, - 169, - 77, - 222, - 235, - 99, - 118 - ] - }, - { - "name": "DistributeCreatorFeesEvent", - "discriminator": [ - 165, - 55, - 129, - 112, - 4, - 179, - 202, - 40 - ] - }, - { - "name": "ExtendAccountEvent", - "discriminator": [ - 97, - 97, - 215, - 144, - 93, - 146, - 22, - 124 - ] - }, - { - "name": "InitUserVolumeAccumulatorEvent", - "discriminator": [ - 134, - 36, - 13, - 72, - 232, - 101, - 130, - 216 - ] - }, - { - "name": "MigrateBondingCurveCreatorEvent", - "discriminator": [ - 155, - 167, - 104, - 220, - 213, - 108, - 243, - 3 - ] - }, - { - "name": "MinimumDistributableFeeEvent", - "discriminator": [ - 168, - 216, - 132, - 239, - 235, - 182, - 49, - 52 - ] - }, - { - "name": "ReservedFeeRecipientsEvent", - "discriminator": [ - 43, - 188, - 250, - 18, - 221, - 75, - 187, - 95 - ] - }, - { - "name": "SetCreatorEvent", - "discriminator": [ - 237, - 52, - 123, - 37, - 245, - 251, - 72, - 210 - ] - }, - { - "name": "SetMetaplexCreatorEvent", - "discriminator": [ - 142, - 203, - 6, - 32, - 127, - 105, - 191, - 162 - ] - }, - { - "name": "SetParamsEvent", - "discriminator": [ - 223, - 195, - 159, - 246, - 62, - 48, - 143, - 131 - ] - }, - { - "name": "SyncUserVolumeAccumulatorEvent", - "discriminator": [ - 197, - 122, - 167, - 124, - 116, - 81, - 91, - 255 - ] - }, - { - "name": "TradeEvent", - "discriminator": [ - 189, - 219, - 127, - 211, - 78, - 230, - 97, - 238 - ] - }, - { - "name": "UpdateGlobalAuthorityEvent", - "discriminator": [ - 182, - 195, - 137, - 42, - 35, - 206, - 207, - 247 - ] - }, - { - "name": "UpdateMayhemVirtualParamsEvent", - "discriminator": [ - 117, - 123, - 228, - 182, - 161, - 168, - 220, - 214 - ] - } - ], - "errors": [ - { - "code": 6000, - "name": "NotAuthorized", - "msg": "The given account is not authorized to execute this instruction." - }, - { - "code": 6001, - "name": "AlreadyInitialized", - "msg": "The program is already initialized." - }, - { - "code": 6002, - "name": "TooMuchSolRequired", - "msg": "slippage: Too much SOL required to buy the given amount of tokens." - }, - { - "code": 6003, - "name": "TooLittleSolReceived", - "msg": "slippage: Too little SOL received to sell the given amount of tokens." - }, - { - "code": 6004, - "name": "MintDoesNotMatchBondingCurve", - "msg": "The mint does not match the bonding curve." - }, - { - "code": 6005, - "name": "BondingCurveComplete", - "msg": "The bonding curve has completed and liquidity migrated to raydium." - }, - { - "code": 6006, - "name": "BondingCurveNotComplete", - "msg": "The bonding curve has not completed." - }, - { - "code": 6007, - "name": "NotInitialized", - "msg": "The program is not initialized." - }, - { - "code": 6008, - "name": "WithdrawTooFrequent", - "msg": "Withdraw too frequent" - }, - { - "code": 6009, - "name": "NewSizeShouldBeGreaterThanCurrentSize", - "msg": "new_size should be > current_size" - }, - { - "code": 6010, - "name": "AccountTypeNotSupported", - "msg": "Account type not supported" - }, - { - "code": 6011, - "name": "InitialRealTokenReservesShouldBeLessThanTokenTotalSupply", - "msg": "initial_real_token_reserves should be less than token_total_supply" - }, - { - "code": 6012, - "name": "InitialVirtualTokenReservesShouldBeGreaterThanInitialRealTokenReserves", - "msg": "initial_virtual_token_reserves should be greater than initial_real_token_reserves" - }, - { - "code": 6013, - "name": "FeeBasisPointsGreaterThanMaximum", - "msg": "fee_basis_points greater than maximum" - }, - { - "code": 6014, - "name": "AllZerosWithdrawAuthority", - "msg": "Withdraw authority cannot be set to System Program ID" - }, - { - "code": 6015, - "name": "PoolMigrationFeeShouldBeLessThanFinalRealSolReserves", - "msg": "pool_migration_fee should be less than final_real_sol_reserves" - }, - { - "code": 6016, - "name": "PoolMigrationFeeShouldBeGreaterThanCreatorFeePlusMaxMigrateFees", - "msg": "pool_migration_fee should be greater than creator_fee + MAX_MIGRATE_FEES" - }, - { - "code": 6017, - "name": "DisabledWithdraw", - "msg": "Migrate instruction is disabled" - }, - { - "code": 6018, - "name": "DisabledMigrate", - "msg": "Migrate instruction is disabled" - }, - { - "code": 6019, - "name": "InvalidCreator", - "msg": "Invalid creator pubkey" - }, - { - "code": 6020, - "name": "BuyZeroAmount", - "msg": "Buy zero amount" - }, - { - "code": 6021, - "name": "NotEnoughTokensToBuy", - "msg": "Not enough tokens to buy" - }, - { - "code": 6022, - "name": "SellZeroAmount", - "msg": "Sell zero amount" - }, - { - "code": 6023, - "name": "NotEnoughTokensToSell", - "msg": "Not enough tokens to sell" - }, - { - "code": 6024, - "name": "Overflow", - "msg": "Overflow" - }, - { - "code": 6025, - "name": "Truncation", - "msg": "Truncation" - }, - { - "code": 6026, - "name": "DivisionByZero", - "msg": "Division by zero" - }, - { - "code": 6027, - "name": "NotEnoughRemainingAccounts", - "msg": "Not enough remaining accounts" - }, - { - "code": 6028, - "name": "AllFeeRecipientsShouldBeNonZero", - "msg": "All fee recipients should be non-zero" - }, - { - "code": 6029, - "name": "UnsortedNotUniqueFeeRecipients", - "msg": "Unsorted or not unique fee recipients" - }, - { - "code": 6030, - "name": "CreatorShouldNotBeZero", - "msg": "Creator should not be zero" - }, - { - "code": 6031, - "name": "StartTimeInThePast" - }, - { - "code": 6032, - "name": "EndTimeInThePast" - }, - { - "code": 6033, - "name": "EndTimeBeforeStartTime" - }, - { - "code": 6034, - "name": "TimeRangeTooLarge" - }, - { - "code": 6035, - "name": "EndTimeBeforeCurrentDay" - }, - { - "code": 6036, - "name": "SupplyUpdateForFinishedRange" - }, - { - "code": 6037, - "name": "DayIndexAfterEndIndex" - }, - { - "code": 6038, - "name": "DayInActiveRange" - }, - { - "code": 6039, - "name": "InvalidIncentiveMint" - }, - { - "code": 6040, - "name": "BuyNotEnoughSolToCoverRent", - "msg": "Buy: Not enough SOL to cover for rent exemption." - }, - { - "code": 6041, - "name": "BuyNotEnoughSolToCoverFees", - "msg": "Buy: Not enough SOL to cover for fees." - }, - { - "code": 6042, - "name": "BuySlippageBelowMinTokensOut", - "msg": "Slippage: Would buy less tokens than expected min_tokens_out" - }, - { - "code": 6043, - "name": "NameTooLong" - }, - { - "code": 6044, - "name": "SymbolTooLong" - }, - { - "code": 6045, - "name": "UriTooLong" - }, - { - "code": 6046, - "name": "CreateV2Disabled" - }, - { - "code": 6047, - "name": "CpitializeMayhemFailed" - }, - { - "code": 6048, - "name": "MayhemModeDisabled" - }, - { - "code": 6049, - "name": "CreatorMigratedToSharingConfig", - "msg": "creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead" - }, - { - "code": 6050, - "name": "UnableToDistributeCreatorVaultMigratedToSharingConfig", - "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" - }, - { - "code": 6051, - "name": "SharingConfigNotActive", - "msg": "Sharing config is not active" - }, - { - "code": 6052, - "name": "UnableToDistributeCreatorFeesToExecutableRecipient", - "msg": "The recipient account is executable, so it cannot receive lamports, remove it from the team first" - }, - { - "code": 6053, - "name": "BondingCurveAndSharingConfigCreatorMismatch", - "msg": "Bonding curve creator does not match sharing config" - }, - { - "code": 6054, - "name": "ShareholdersAndRemainingAccountsMismatch", - "msg": "Remaining accounts do not match shareholders, make sure to pass exactly the same pubkeys in the same order" - }, - { - "code": 6055, - "name": "InvalidShareBps", - "msg": "Share bps must be greater than 0" - }, - { - "code": 6056, - "name": "CashbackNotEnabled", - "msg": "Cashback is not enabled" - } - ], - "types": [ - { - "name": "AdminSetCreatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "admin_set_creator_authority", - "type": "pubkey" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "old_creator", - "type": "pubkey" - }, - { - "name": "new_creator", - "type": "pubkey" - } - ] - } - }, - { - "name": "AdminSetIdlAuthorityEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "idl_authority", - "type": "pubkey" - } - ] - } - }, - { - "name": "AdminUpdateTokenIncentivesEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "start_time", - "type": "i64" - }, - { - "name": "end_time", - "type": "i64" - }, - { - "name": "day_number", - "type": "u64" - }, - { - "name": "token_supply_per_day", - "type": "u64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "seconds_in_a_day", - "type": "i64" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "BondingCurve", - "type": { - "kind": "struct", - "fields": [ - { - "name": "virtual_token_reserves", - "type": "u64" - }, - { - "name": "virtual_sol_reserves", - "type": "u64" - }, - { - "name": "real_token_reserves", - "type": "u64" - }, - { - "name": "real_sol_reserves", - "type": "u64" - }, - { - "name": "token_total_supply", - "type": "u64" - }, - { - "name": "complete", - "type": "bool" - }, - { - "name": "creator", - "type": "pubkey" - }, - { - "name": "is_mayhem_mode", - "type": "bool" - }, - { - "name": "is_cashback_coin", - "type": "bool" - } - ] - } - }, - { - "name": "ClaimCashbackEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "amount", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "total_claimed", - "type": "u64" - }, - { - "name": "total_cashback_earned", - "type": "u64" - } - ] - } - }, - { - "name": "ClaimTokenIncentivesEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "amount", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "total_claimed_tokens", - "type": "u64" - }, - { - "name": "current_sol_volume", - "type": "u64" - } - ] - } - }, - { - "name": "CloseUserVolumeAccumulatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "total_unclaimed_tokens", - "type": "u64" - }, - { - "name": "total_claimed_tokens", - "type": "u64" - }, - { - "name": "current_sol_volume", - "type": "u64" - }, - { - "name": "last_update_timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "CollectCreatorFeeEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "creator", - "type": "pubkey" - }, - { - "name": "creator_fee", - "type": "u64" - } - ] - } - }, - { - "name": "CompleteEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "CompletePumpAmmMigrationEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "mint_amount", - "type": "u64" - }, - { - "name": "sol_amount", - "type": "u64" - }, - { - "name": "pool_migration_fee", - "type": "u64" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "pool", - "type": "pubkey" - } - ] - } - }, - { - "name": "ConfigStatus", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Paused" - }, - { - "name": "Active" - } - ] - } - }, - { - "name": "CreateEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, - { - "name": "uri", - "type": "string" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "user", - "type": "pubkey" - }, - { - "name": "creator", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "virtual_token_reserves", - "type": "u64" - }, - { - "name": "virtual_sol_reserves", - "type": "u64" - }, - { - "name": "real_token_reserves", - "type": "u64" - }, - { - "name": "token_total_supply", - "type": "u64" - }, - { - "name": "token_program", - "type": "pubkey" - }, - { - "name": "is_mayhem_mode", - "type": "bool" - }, - { - "name": "is_cashback_enabled", - "type": "bool" - } - ] - } - }, - { - "name": "DistributeCreatorFeesEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "sharing_config", - "type": "pubkey" - }, - { - "name": "admin", - "type": "pubkey" - }, - { - "name": "shareholders", - "type": { - "vec": { - "defined": { - "name": "Shareholder" - } - } - } - }, - { - "name": "distributed", - "type": "u64" - } - ] - } - }, - { - "name": "ExtendAccountEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "account", - "type": "pubkey" - }, - { - "name": "user", - "type": "pubkey" - }, - { - "name": "current_size", - "type": "u64" - }, - { - "name": "new_size", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "FeeConfig", - "type": { - "kind": "struct", - "fields": [ - { - "name": "bump", - "type": "u8" - }, - { - "name": "admin", - "type": "pubkey" - }, - { - "name": "flat_fees", - "type": { - "defined": { - "name": "Fees" - } - } - }, - { - "name": "fee_tiers", - "type": { - "vec": { - "defined": { - "name": "FeeTier" - } - } - } - } - ] - } - }, - { - "name": "FeeTier", - "type": { - "kind": "struct", - "fields": [ - { - "name": "market_cap_lamports_threshold", - "type": "u128" - }, - { - "name": "fees", - "type": { - "defined": { - "name": "Fees" - } - } - } - ] - } - }, - { - "name": "Fees", - "type": { - "kind": "struct", - "fields": [ - { - "name": "lp_fee_bps", - "type": "u64" - }, - { - "name": "protocol_fee_bps", - "type": "u64" - }, - { - "name": "creator_fee_bps", - "type": "u64" - } - ] - } - }, - { - "name": "Global", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initialized", - "docs": [ - "Unused" - ], - "type": "bool" - }, - { - "name": "authority", - "type": "pubkey" - }, - { - "name": "fee_recipient", - "type": "pubkey" - }, - { - "name": "initial_virtual_token_reserves", - "type": "u64" - }, - { - "name": "initial_virtual_sol_reserves", - "type": "u64" - }, - { - "name": "initial_real_token_reserves", - "type": "u64" - }, - { - "name": "token_total_supply", - "type": "u64" - }, - { - "name": "fee_basis_points", - "type": "u64" - }, - { - "name": "withdraw_authority", - "type": "pubkey" - }, - { - "name": "enable_migrate", - "docs": [ - "Unused" - ], - "type": "bool" - }, - { - "name": "pool_migration_fee", - "type": "u64" - }, - { - "name": "creator_fee_basis_points", - "type": "u64" - }, - { - "name": "fee_recipients", - "type": { - "array": [ - "pubkey", - 7 - ] - } - }, - { - "name": "set_creator_authority", - "type": "pubkey" - }, - { - "name": "admin_set_creator_authority", - "type": "pubkey" - }, - { - "name": "create_v2_enabled", - "type": "bool" - }, - { - "name": "whitelist_pda", - "type": "pubkey" - }, - { - "name": "reserved_fee_recipient", - "type": "pubkey" - }, - { - "name": "mayhem_mode_enabled", - "type": "bool" - }, - { - "name": "reserved_fee_recipients", - "type": { - "array": [ - "pubkey", - 7 - ] - } - }, - { - "name": "is_cashback_enabled", - "type": "bool" - } - ] - } - }, - { - "name": "GlobalVolumeAccumulator", - "type": { - "kind": "struct", - "fields": [ - { - "name": "start_time", - "type": "i64" - }, - { - "name": "end_time", - "type": "i64" - }, - { - "name": "seconds_in_a_day", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "total_token_supply", - "type": { - "array": [ - "u64", - 30 - ] - } - }, - { - "name": "sol_volumes", - "type": { - "array": [ - "u64", - 30 - ] - } - } - ] - } - }, - { - "name": "InitUserVolumeAccumulatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "payer", - "type": "pubkey" - }, - { - "name": "user", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "MigrateBondingCurveCreatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "sharing_config", - "type": "pubkey" - }, - { - "name": "old_creator", - "type": "pubkey" - }, - { - "name": "new_creator", - "type": "pubkey" - } - ] - } - }, - { - "name": "MinimumDistributableFeeEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "minimum_required", - "type": "u64" - }, - { - "name": "distributable_fees", - "type": "u64" - }, - { - "name": "can_distribute", - "type": "bool" - } - ] - } - }, - { - "name": "OptionBool", - "type": { - "kind": "struct", - "fields": [ - "bool" - ] - } - }, - { - "name": "ReservedFeeRecipientsEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "reserved_fee_recipient", - "type": "pubkey" - }, - { - "name": "reserved_fee_recipients", - "type": { - "array": [ - "pubkey", - 7 - ] - } - } - ] - } - }, - { - "name": "SetCreatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "creator", - "type": "pubkey" - } - ] - } - }, - { - "name": "SetMetaplexCreatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "bonding_curve", - "type": "pubkey" - }, - { - "name": "metadata", - "type": "pubkey" - }, - { - "name": "creator", - "type": "pubkey" - } - ] - } - }, - { - "name": "SetParamsEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initial_virtual_token_reserves", - "type": "u64" - }, - { - "name": "initial_virtual_sol_reserves", - "type": "u64" - }, - { - "name": "initial_real_token_reserves", - "type": "u64" - }, - { - "name": "final_real_sol_reserves", - "type": "u64" - }, - { - "name": "token_total_supply", - "type": "u64" - }, - { - "name": "fee_basis_points", - "type": "u64" - }, - { - "name": "withdraw_authority", - "type": "pubkey" - }, - { - "name": "enable_migrate", - "type": "bool" - }, - { - "name": "pool_migration_fee", - "type": "u64" - }, - { - "name": "creator_fee_basis_points", - "type": "u64" - }, - { - "name": "fee_recipients", - "type": { - "array": [ - "pubkey", - 8 - ] - } - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "set_creator_authority", - "type": "pubkey" - }, - { - "name": "admin_set_creator_authority", - "type": "pubkey" - } - ] - } - }, - { - "name": "Shareholder", - "type": { - "kind": "struct", - "fields": [ - { - "name": "address", - "type": "pubkey" - }, - { - "name": "share_bps", - "type": "u16" - } - ] - } - }, - { - "name": "SharingConfig", - "type": { - "kind": "struct", - "fields": [ - { - "name": "bump", - "type": "u8" - }, - { - "name": "version", - "type": "u8" - }, - { - "name": "status", - "type": { - "defined": { - "name": "ConfigStatus" - } - } - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "admin", - "type": "pubkey" - }, - { - "name": "admin_revoked", - "type": "bool" - }, - { - "name": "shareholders", - "type": { - "vec": { - "defined": { - "name": "Shareholder" - } - } - } - } - ] - } - }, - { - "name": "SyncUserVolumeAccumulatorEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "total_claimed_tokens_before", - "type": "u64" - }, - { - "name": "total_claimed_tokens_after", - "type": "u64" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "TradeEvent", - "docs": [ - "ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\"" - ], - "type": { - "kind": "struct", - "fields": [ - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "sol_amount", - "type": "u64" - }, - { - "name": "token_amount", - "type": "u64" - }, - { - "name": "is_buy", - "type": "bool" - }, - { - "name": "user", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "virtual_sol_reserves", - "type": "u64" - }, - { - "name": "virtual_token_reserves", - "type": "u64" - }, - { - "name": "real_sol_reserves", - "type": "u64" - }, - { - "name": "real_token_reserves", - "type": "u64" - }, - { - "name": "fee_recipient", - "type": "pubkey" - }, - { - "name": "fee_basis_points", - "type": "u64" - }, - { - "name": "fee", - "type": "u64" - }, - { - "name": "creator", - "type": "pubkey" - }, - { - "name": "creator_fee_basis_points", - "type": "u64" - }, - { - "name": "creator_fee", - "type": "u64" - }, - { - "name": "track_volume", - "type": "bool" - }, - { - "name": "total_unclaimed_tokens", - "type": "u64" - }, - { - "name": "total_claimed_tokens", - "type": "u64" - }, - { - "name": "current_sol_volume", - "type": "u64" - }, - { - "name": "last_update_timestamp", - "type": "i64" - }, - { - "name": "ix_name", - "type": "string" - }, - { - "name": "mayhem_mode", - "type": "bool" - }, - { - "name": "cashback_fee_basis_points", - "type": "u64" - }, - { - "name": "cashback", - "type": "u64" - } - ] - } - }, - { - "name": "UpdateGlobalAuthorityEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "global", - "type": "pubkey" - }, - { - "name": "authority", - "type": "pubkey" - }, - { - "name": "new_authority", - "type": "pubkey" - }, - { - "name": "timestamp", - "type": "i64" - } - ] - } - }, - { - "name": "UpdateMayhemVirtualParamsEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "timestamp", - "type": "i64" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "virtual_token_reserves", - "type": "u64" - }, - { - "name": "virtual_sol_reserves", - "type": "u64" - }, - { - "name": "new_virtual_token_reserves", - "type": "u64" - }, - { - "name": "new_virtual_sol_reserves", - "type": "u64" - }, - { - "name": "real_token_reserves", - "type": "u64" - }, - { - "name": "real_sol_reserves", - "type": "u64" - } - ] - } - }, - { - "name": "UserVolumeAccumulator", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey" - }, - { - "name": "needs_claim", - "type": "bool" - }, - { - "name": "total_unclaimed_tokens", - "type": "u64" - }, - { - "name": "total_claimed_tokens", - "type": "u64" - }, - { - "name": "current_sol_volume", - "type": "u64" - }, - { - "name": "last_update_timestamp", - "type": "i64" - }, - { - "name": "has_total_claimed_tokens", - "type": "bool" - }, - { - "name": "cashback_earned", - "type": "u64" - }, - { - "name": "total_cashback_claimed", - "type": "u64" - } - ] - } - } - ] +{ + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "metadata": { + "name": "pump", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "add_quote_control_mint", + "discriminator": [ + 2, + 14, + 61, + 138, + 170, + 142, + 14, + 95 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "quote_control", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 113, + 117, + 111, + 116, + 101, + 45, + 99, + 111, + 110, + 116, + 114, + 111, + 108 + ] + } + ] + } + }, + { + "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": "quote_mint", + "type": "pubkey" + }, + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + } + ] + }, + { + "name": "add_quote_mint", + "discriminator": [ + 111, + 121, + 21, + 56, + 40, + 24, + 94, + 209 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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_mint", + "type": "pubkey" + } + ] + }, + { + "name": "admin_cto", + "discriminator": [ + 125, + 126, + 214, + 134, + 77, + 229, + 188, + 89 + ], + "accounts": [ + { + "name": "admin_set_creator_authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "current_creator", + "docs": [ + "Not declared writable: a pre-creator (legacy) curve stores the zero key, which is the", + "system program, whose write lock the runtime always demotes. Callers MUST still pass this", + "account as writable whenever it is a wallet, so the outgoing creator can be paid from its", + "vaults; otherwise the instruction fails with `CtoCreatorAccountNotWritable`. A creator that", + "is not a system-owned wallet (a program, a sysvar, a pump-fees or other program-owned", + "account) is skipped, its vault balances stay collectable, and it may be passed read-only." + ] + }, + { + "name": "current_creator_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "current_creator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "current_creator" + } + ] + } + }, + { + "name": "creator_vault_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "holder_creator_vault", + "docs": [ + "`[\"creator-vault\", find_program_address([\"holder-rewards\", mint], pump)]`. The sweep", + "destination on the holder path of a fee-shared coin, ignored otherwise. Re-derived in the", + "handler before it is written to; kept out of the seeds constraints to stay under the sBPF", + "stack frame." + ], + "writable": true + }, + { + "name": "holder_creator_vault_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "holder_creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pump_amm", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "amm_global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pool_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108 + ] + }, + { + "kind": "const", + "value": [ + 0, + 0 + ] + }, + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pump_amm_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "coin_creator_vault_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 95, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "current_creator" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "coin_creator_vault_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "coin_creator_vault_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "sharing_config", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "pump_fees" + } + } + }, + { + "name": "pump_fees", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "name": "pump_fees_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_fees" + } + } + }, + { + "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": "is_holder_reward", + "type": { + "option": "bool" + } + }, + { + "name": "creator_fee_bps", + "type": { + "option": "u64" + } + }, + { + "name": "new_creator", + "type": { + "option": "pubkey" + } + } + ] + }, + { + "name": "admin_set_idl_authority", + "discriminator": [ + 8, + 217, + 96, + 231, + 144, + 104, + 192, + 5 + ], + "accounts": [ + { + "name": "authority", + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "idl_account", + "writable": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "program_signer", + "pda": { + "seeds": [] + } + }, + { + "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": "idl_authority", + "type": "pubkey" + } + ] + }, + { + "name": "admin_update_token_incentives", + "discriminator": [ + 209, + 11, + 115, + 87, + 213, + 23, + 124, + 204 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "global_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "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": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "pump_token_supply_per_day", + "type": "u64" + } + ] + }, + { + "name": "buy", + "docs": [ + "Buys tokens from a bonding curve." + ], + "discriminator": [ + 102, + 6, + 61, + 18, + 1, + 218, + 235, + 234 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "max_sol_cost", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "buy_exact_quote_in_v2", + "discriminator": [ + 194, + 171, + 28, + 70, + 104, + 77, + 91, + 47 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "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": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "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": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_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": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "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": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "docs": [ + "canonical SPL associated-token PDA. Validated in handlers via", + "`validate_user_quote_token_account` for non-legacy mints; ignored for legacy (SOL) trades." + ], + "writable": true + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "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": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "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": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "spendable_quote_in", + "type": "u64" + }, + { + "name": "min_tokens_out", + "type": "u64" + } + ] + }, + { + "name": "buy_exact_sol_in", + "docs": [ + "Given a budget of spendable SOL, buy at least min_tokens_out tokens.", + "Fees are deducted from spendable_sol_in.", + "", + "# Quote formulas", + "Where:", + "- total_fee_bps = protocol_fee_bps + creator_fee_bps (creator_fee_bps is 0 if no creator)", + "- floor(a/b) = a / b (integer division)", + "- ceil(a/b) = (a + b - 1) / b", + "", + "SOL → tokens quote", + "To calculate tokens_out for a given spendable_sol_in:", + "1. net_sol = floor(spendable_sol_in * 10_000 / (10_000 + total_fee_bps))", + "2. fees = ceil(net_sol * protocol_fee_bps / 10_000) + ceil(net_sol * creator_fee_bps / 10_000) (creator_fee_bps is 0 if no creator)", + "3. if net_sol + fees > spendable_sol_in: net_sol = net_sol - (net_sol + fees - spendable_sol_in)", + "4. tokens_out = floor((net_sol - 1) * virtual_token_reserves / (virtual_sol_reserves + net_sol - 1))", + "", + "Reverse quote (tokens → SOL)", + "To calculate spendable_sol_in for a desired number of tokens:", + "1. net_sol = ceil(tokens * virtual_sol_reserves / (virtual_token_reserves - tokens)) + 1", + "2. spendable_sol_in = ceil(net_sol * (10_000 + total_fee_bps) / 10_000)", + "", + "Rent", + "Separately make sure the instruction's payer has enough SOL to cover rent for:", + "- creator_vault: rent.minimum_balance(0)", + "- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)" + ], + "discriminator": [ + 56, + 252, + 116, + 8, + 158, + 223, + 205, + 95 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "spendable_sol_in", + "type": "u64" + }, + { + "name": "min_tokens_out", + "type": "u64" + }, + { + "name": "track_volume", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "buy_v2", + "discriminator": [ + 184, + 23, + 238, + 97, + 103, + 197, + 211, + 61 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "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": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "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": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_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": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "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": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "docs": [ + "canonical SPL associated-token PDA. Validated in handlers via", + "`validate_user_quote_token_account` for non-legacy mints; ignored for legacy (SOL) trades." + ], + "writable": true + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "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": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "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": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "max_sol_cost", + "type": "u64" + } + ] + }, + { + "name": "claim_cashback", + "discriminator": [ + 37, + 58, + 35, + 126, + 190, + 53, + 228, + 197 + ], + "accounts": [ + { + "name": "user", + "writable": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [] + }, + { + "name": "claim_cashback_v2", + "docs": [ + "Pays out the user's accrued cashback. For a token quote, `associated_quote_user` may be", + "any token account of `quote_mint` owned by `user`, not only the associated one." + ], + "discriminator": [ + 122, + 243, + 204, + 65, + 94, + 116, + 29, + 55 + ], + "accounts": [ + { + "name": "user", + "writable": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "associated_quote_user", + "writable": true + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [] + }, + { + "name": "claim_token_incentives", + "discriminator": [ + 16, + 4, + 71, + 28, + 204, + 1, + 40, + 27 + ], + "accounts": [ + { + "name": "user" + }, + { + "name": "user_ata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "global_incentive_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "global_volume_accumulator" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "mint", + "relations": [ + "global_volume_accumulator" + ] + }, + { + "name": "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "payer", + "writable": true, + "signer": true + } + ], + "args": [] + }, + { + "name": "close_user_volume_accumulator", + "discriminator": [ + 249, + 69, + 164, + 218, + 150, + 103, + 84, + 138 + ], + "accounts": [ + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "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": "collect_creator_fee", + "docs": [ + "Collects creator_fee from creator_vault to the coin creator account" + ], + "discriminator": [ + 20, + 22, + 86, + 123, + 198, + 28, + 219, + 132 + ], + "accounts": [ + { + "name": "creator", + "writable": true + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "creator" + } + ] + } + }, + { + "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": "collect_creator_fee_v2", + "docs": [ + "Collects creator_fee from creator_vault to the coin creator account" + ], + "discriminator": [ + 207, + 17, + 138, + 242, + 4, + 34, + 19, + 56 + ], + "accounts": [ + { + "name": "creator", + "writable": true + }, + { + "name": "creator_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "creator" + } + ] + } + }, + { + "name": "creator_vault_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "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": "create", + "docs": [ + "Creates a new coin and bonding curve." + ], + "discriminator": [ + 24, + 30, + 200, + 40, + 5, + 28, + 7, + 119 + ], + "accounts": [ + { + "name": "mint", + "writable": true, + "signer": true + }, + { + "name": "mint_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 105, + 110, + 116, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "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": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "mpl_token_metadata", + "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" + }, + { + "name": "metadata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "mpl_token_metadata" + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "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": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + }, + { + "name": "create_v2", + "docs": [ + "Creates a new spl-22 coin and bonding curve.", + "", + "Remaining accounts select the quote mint (none = SOL): `quote_mint`,", + "`associated_quote_bonding_curve`, `quote_token_program` (SPL Token or Token-2022; must own", + "the mint), plus an optional fourth account, the `quote-control` PDA, read only when `Global`", + "does not whitelist the mint. A mint admitted through quote-control seeds the curve's", + "virtual quote reserves from its quote-control entry instead of `Global`, and cannot be", + "used with `is_mayhem_mode` (`MayhemModeQuoteMintNotAllowed`). The Token-2022", + "native mint is rejected, and a Token-2022 quote mint may only carry the xStock operable", + "extension set (metadata pointer/metadata, permanent delegate, initialized default account", + "state, scaled UI amount, pausable, confidential-transfer mint, and a transfer hook with no", + "program). The trailing `creator_fee_bps` argument (EOF-tolerant) sets the coin's own creator", + "fee rate for a quote mint admitted through quote-control, and then requires", + "`Global.creator_fee_configurable`, a non-cashback coin and a value in", + "`1..=Global.max_configurable_creator_fee_bps`; on a SOL or `Global`-whitelisted quote it is", + "ignored, and omitted or zero stores 0 so the pump-fees schedule rate applies.", + "`is_cashback_enabled` is deprecated and must be false. `is_holder_reward` (EOF-tolerant,", + "gated by `Global.is_holder_reward_enabled`) sets the creator to the `holder-rewards` PDA", + "of the mint, so creator fees accrue to its creator vault, are collected onto the PDA with", + "`collect_creator_fee*` and paid out through `distribute_fee_to_holders`." + ], + "discriminator": [ + 214, + 144, + 76, + 236, + 95, + 139, + 49, + 180 + ], + "accounts": [ + { + "name": "mint", + "writable": true, + "signer": true + }, + { + "name": "mint_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 105, + 110, + 116, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "mayhem_program_id", + "writable": true, + "address": "MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e" + }, + { + "name": "global_params", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 45, + 112, + 97, + 114, + 97, + 109, + 115 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 5, + 42, + 229, + 215, + 167, + 218, + 167, + 36, + 166, + 234, + 176, + 167, + 41, + 84, + 145, + 133, + 90, + 212, + 160, + 103, + 22, + 96, + 103, + 76, + 78, + 3, + 69, + 89, + 128, + 61, + 101, + 163 + ] + } + } + }, + { + "name": "sol_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 111, + 108, + 45, + 118, + 97, + 117, + 108, + 116 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 5, + 42, + 229, + 215, + 167, + 218, + 167, + 36, + 166, + 234, + 176, + 167, + 41, + 84, + 145, + 133, + 90, + 212, + 160, + 103, + 22, + 96, + 103, + 76, + 78, + 3, + 69, + 89, + 128, + 61, + 101, + 163 + ] + } + } + }, + { + "name": "mayhem_state", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 97, + 121, + 104, + 101, + 109, + 45, + 115, + 116, + 97, + 116, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 5, + 42, + 229, + 215, + 167, + 218, + 167, + 36, + 166, + 234, + 176, + 167, + 41, + 84, + 145, + 133, + 90, + 212, + 160, + 103, + 22, + 96, + 103, + 76, + 78, + 3, + 69, + 89, + 128, + 61, + 101, + 163 + ] + } + } + }, + { + "name": "mayhem_token_vault", + "writable": true + }, + { + "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": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_enabled", + "type": { + "defined": { + "name": "OptionBool" + } + } + }, + { + "name": "creator_fee_bps", + "type": { + "defined": { + "name": "OptionU64" + } + } + }, + { + "name": "is_holder_reward", + "type": { + "defined": { + "name": "OptionBool" + } + } + } + ] + }, + { + "name": "distribute_creator_fees", + "docs": [ + "Distributes creator fees to shareholders based on their share percentages", + "The creator vault needs to have at least the minimum distributable amount to distribute fees", + "This can be checked with the get_minimum_distributable_fee instruction" + ], + "discriminator": [ + 165, + 114, + 103, + 0, + 121, + 206, + 247, + 81 + ], + "accounts": [ + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [], + "returns": { + "defined": { + "name": "DistributeCreatorFeesEvent" + } + } + }, + { + "name": "distribute_creator_fees_v2", + "discriminator": [ + 255, + 203, + 19, + 79, + 244, + 68, + 8, + 159 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "creator_vault_quote_token_account", + "docs": [ + "Deserialized manually in the handler for non-legacy quote mints." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [ + { + "name": "initialize_ata", + "type": "bool" + } + ], + "returns": { + "defined": { + "name": "DistributeCreatorFeesEvent" + } + } + }, + { + "name": "distribute_fee_to_holders", + "docs": [ + "Pays fees collected on the `holder-rewards` PDA (via `collect_creator_fee*` with the PDA", + "as creator) out to holders: `amounts[i]` goes to remaining accounts `[2i]` (owner) /", + "`[2i + 1]` (its quote ATA, created if missing). `holder_rewards_token_account` is any", + "quote token account owned by the PDA (normally its ATA): the source on a token quote, and", + "on a SOL quote a parked WSOL account closed into the PDA first; pass the program id when", + "there is none. Signed by `Global.holder_reward_claim_authority`." + ], + "discriminator": [ + 98, + 54, + 145, + 97, + 2, + 70, + 173, + 43 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "holder_reward_claim_authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "mint" + }, + { + "name": "holder_rewards", + "docs": [ + "deliver the collected fees here (lamports on a SOL quote)" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 104, + 111, + 108, + 100, + 101, + 114, + 45, + 114, + 101, + 119, + 97, + 114, + 100, + 115 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "holder_rewards_token_account", + "writable": true, + "optional": true + }, + { + "name": "quote_mint" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "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": "amounts", + "type": { + "vec": "u64" + } + } + ] + }, + { + "name": "extend_account", + "docs": [ + "Extends the size of program-owned accounts" + ], + "discriminator": [ + 234, + 102, + 194, + 203, + 150, + 72, + 62, + 229 + ], + "accounts": [ + { + "name": "account", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "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": "get_minimum_distributable_fee", + "docs": [ + "Permissionless instruction to check the minimum required fees for distribution", + "Returns the minimum required balance from the creator_vault and whether distribution can proceed" + ], + "discriminator": [ + 117, + 225, + 127, + 202, + 134, + 95, + 68, + 35 + ], + "accounts": [ + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "bonding_curve", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "creator_vault", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + } + ], + "args": [], + "returns": { + "defined": { + "name": "MinimumDistributableFeeEvent" + } + } + }, + { + "name": "init_user_volume_accumulator", + "discriminator": [ + 94, + 6, + 202, + 115, + 255, + 96, + 232, + 183 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "user" + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "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": "initialize", + "docs": [ + "Creates the global state." + ], + "discriminator": [ + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "initialize_quote_control", + "discriminator": [ + 239, + 73, + 245, + 173, + 209, + 177, + 84, + 66 + ], + "accounts": [ + { + "name": "quote_control", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 113, + 117, + 111, + 116, + 101, + 45, + 99, + 111, + 110, + 116, + 114, + 111, + 108 + ] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "migrate", + "docs": [ + "Migrates liquidity to pump_amm if the bonding curve is complete" + ], + "discriminator": [ + 155, + 234, + 231, + 146, + 236, + 158, + 162, + 30 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "withdraw_authority", + "writable": true, + "relations": [ + "global" + ] + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "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": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "pump_amm", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108 + ] + }, + { + "kind": "const", + "value": [ + 0, + 0 + ] + }, + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pool_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "pool_authority_mint_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_authority_wsol_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "amm_global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "wsol_mint", + "address": "So11111111111111111111111111111111111111112" + }, + { + "name": "lp_mint", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "user_pool_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_2022_program" + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_base_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "mint" + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "wsol_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "pump_amm_event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "migrate_bonding_curve_creator", + "discriminator": [ + 87, + 124, + 52, + 191, + 52, + 38, + 214, + 232 + ], + "accounts": [ + { + "name": "mint", + "relations": [ + "sharing_config" + ] + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "sharing_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "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": "migrate_v2", + "docs": [ + "Migrates liquidity to pump_amm if the bonding curve is complete" + ], + "discriminator": [ + 187, + 203, + 18, + 31, + 206, + 237, + 254, + 41 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "withdraw_authority", + "writable": true, + "relations": [ + "global" + ] + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_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": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "pump_amm", + "address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" + }, + { + "name": "pool", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108 + ] + }, + { + "kind": "const", + "value": [ + 0, + 0 + ] + }, + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "base_mint" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "pool_authority", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 45, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "pool_authority_mint_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_authority_quote_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "amm_global_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "lp_mint", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 111, + 108, + 95, + 108, + 112, + 95, + 109, + 105, + 110, + 116 + ] + }, + { + "kind": "account", + "path": "pool" + } + ], + "program": { + "kind": "account", + "path": "pump_amm" + } + } + }, + { + "name": "user_pool_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool_authority" + }, + { + "kind": "account", + "path": "token_2022_program" + }, + { + "kind": "account", + "path": "lp_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_base_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "pool_quote_token_account", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "pool" + }, + { + "kind": "account", + "path": "quote_token_program" + }, + { + "kind": "account", + "path": "quote_mint" + } + ], + "program": { + "kind": "account", + "path": "associated_token_program" + } + } + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "token_2022_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "pump_amm_event_authority" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "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": "remove_quote_control_mint", + "discriminator": [ + 223, + 7, + 253, + 26, + 81, + 165, + 218, + 166 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "quote_control", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 113, + 117, + 111, + 116, + 101, + 45, + 99, + 111, + 110, + 116, + 114, + 111, + 108 + ] + } + ] + } + }, + { + "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": "quote_mint", + "type": "pubkey" + } + ] + }, + { + "name": "remove_quote_mint", + "discriminator": [ + 177, + 65, + 223, + 38, + 88, + 209, + 158, + 155 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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_mint", + "type": "pubkey" + } + ] + }, + { + "name": "sell", + "docs": [ + "Sells tokens into a bonding curve.", + "For cashback coins, pass as remaining_accounts: [0] user_volume_accumulator,", + "[1] bonding_curve_v2. If provided and valid, creator_fee goes to user_volume_accumulator.", + "Otherwise, falls back to transferring creator_fee to creator_vault." + ], + "discriminator": [ + 51, + 230, + 133, + 164, + 1, + 127, + 131, + 173 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + }, + { + "name": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "min_sol_output", + "type": "u64" + } + ] + }, + { + "name": "sell_v2", + "discriminator": [ + 93, + 246, + 130, + 60, + 231, + 233, + 64, + 178 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "base_mint" + }, + { + "name": "quote_mint" + }, + { + "name": "base_token_program" + }, + { + "name": "quote_token_program" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "associated_quote_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "fee_recipient" + }, + { + "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": "buyback_fee_recipient", + "writable": true + }, + { + "name": "associated_quote_buyback_fee_recipient", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "buyback_fee_recipient" + }, + { + "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": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ] + } + }, + { + "name": "associated_base_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "account", + "path": "base_token_program" + }, + { + "kind": "account", + "path": "base_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": "associated_quote_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "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": "user", + "writable": true, + "signer": true + }, + { + "name": "associated_base_user", + "writable": true + }, + { + "name": "associated_quote_user", + "docs": [ + "canonical SPL associated-token PDA. Validated in `sell_v2_ix` via", + "`validate_user_quote_token_account` for non-legacy mints; ignored for legacy (SOL) trades." + ], + "writable": true + }, + { + "name": "creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 99, + 114, + 101, + 97, + 116, + 111, + 114, + 45, + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "bonding_curve.creator", + "account": "BondingCurve" + } + ] + } + }, + { + "name": "associated_creator_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "creator_vault" + }, + { + "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": "sharing_config", + "docs": [ + "seeds; the account is intentionally not deserialized here because it may be uninitialized", + "for mints that have not created a fee sharing config. Handlers must check", + "`data_is_empty()` / owner before reading." + ], + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 104, + 97, + 114, + 105, + 110, + 103, + 45, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 12, + 53, + 255, + 169, + 5, + 90, + 142, + 86, + 141, + 168, + 247, + 188, + 7, + 86, + 21, + 39, + 76, + 241, + 201, + 44, + 164, + 31, + 64, + 0, + 156, + 81, + 106, + 164, + 20, + 194, + 124, + 112 + ] + } + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "name": "associated_user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "user_volume_accumulator" + }, + { + "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": "fee_config", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 102, + 101, + 101, + 95, + 99, + 111, + 110, + 102, + 105, + 103 + ] + }, + { + "kind": "const", + "value": [ + 1, + 86, + 224, + 246, + 147, + 102, + 90, + 207, + 68, + 219, + 21, + 104, + 191, + 23, + 91, + 170, + 81, + 137, + 203, + 151, + 245, + 210, + 255, + 59, + 101, + 93, + 43, + 182, + 253, + 109, + 24, + 176 + ] + } + ], + "program": { + "kind": "account", + "path": "fee_program" + } + } + }, + { + "name": "fee_program", + "address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" + }, + { + "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", + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "min_sol_output", + "type": "u64" + } + ] + }, + { + "name": "set_creator", + "docs": [ + "Allows Global::set_creator_authority to set the bonding curve creator from Metaplex metadata or input argument" + ], + "discriminator": [ + 254, + 148, + 255, + 112, + 207, + 142, + 170, + 165 + ], + "accounts": [ + { + "name": "set_creator_authority", + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "metadata", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "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": "creator", + "type": "pubkey" + } + ] + }, + { + "name": "set_mayhem_virtual_params", + "discriminator": [ + 61, + 169, + 188, + 191, + 153, + 149, + 42, + 97 + ], + "accounts": [ + { + "name": "sol_vault_authority", + "writable": true, + "signer": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 115, + 111, + 108, + 45, + 118, + 97, + 117, + 108, + 116 + ] + } + ], + "program": { + "kind": "const", + "value": [ + 5, + 42, + 229, + 215, + 167, + 218, + 167, + 36, + 166, + 234, + 176, + 167, + 41, + 84, + 145, + 133, + 90, + 212, + 160, + 103, + 22, + 96, + 103, + 76, + 78, + 3, + 69, + 89, + 128, + 61, + 101, + 163 + ] + } + } + }, + { + "name": "mayhem_token_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "sol_vault_authority" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "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": "mint" + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "token_program", + "address": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "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_metaplex_creator", + "docs": [ + "Syncs the bonding curve creator with the Metaplex metadata creator if it exists" + ], + "discriminator": [ + 138, + 96, + 174, + 217, + 48, + 85, + 197, + 246 + ], + "accounts": [ + { + "name": "mint" + }, + { + "name": "metadata", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 11, + 112, + 101, + 177, + 227, + 209, + 124, + 69, + 56, + 157, + 82, + 127, + 107, + 4, + 195, + 205, + 88, + 184, + 108, + 115, + 26, + 160, + 253, + 181, + 73, + 182, + 209, + 188, + 3, + 248, + 41, + 70 + ] + } + } + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "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_params", + "docs": [ + "Sets the global state parameters." + ], + "discriminator": [ + 27, + 234, + 178, + 52, + 147, + 2, + 187, + 141 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + } + ] + }, + { + "name": "set_quote_control_admin", + "discriminator": [ + 62, + 79, + 161, + 211, + 165, + 170, + 214, + 210 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "quote_control", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 113, + 117, + 111, + 116, + 101, + 45, + 99, + 111, + 110, + 116, + 114, + 111, + 108 + ] + } + ] + } + }, + { + "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": "new_admin", + "type": "pubkey" + } + ] + }, + { + "name": "set_reserved_fee_recipients", + "discriminator": [ + 111, + 172, + 162, + 232, + 114, + 89, + 213, + 142 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "whitelist_pda", + "type": "pubkey" + } + ] + }, + { + "name": "set_virtual_quote_reserves", + "discriminator": [ + 101, + 135, + 191, + 104, + 9, + 88, + 20, + 96 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "initial_virtual_quote_reserves", + "type": "u64" + } + ] + }, + { + "name": "sync_user_volume_accumulator", + "discriminator": [ + 86, + 31, + 192, + 87, + 163, + 87, + 79, + 238 + ], + "accounts": [ + { + "name": "user" + }, + { + "name": "global_volume_accumulator", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + } + ] + } + }, + { + "name": "user_volume_accumulator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 117, + 115, + 101, + 114, + 95, + 118, + 111, + 108, + 117, + 109, + 101, + 95, + 97, + 99, + 99, + 117, + 109, + 117, + 108, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "user" + } + ] + } + }, + { + "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": "toggle_cashback_enabled", + "discriminator": [ + 115, + 103, + 224, + 255, + 189, + 89, + 86, + 195 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_create_v2", + "discriminator": [ + 28, + 255, + 230, + 240, + 172, + 107, + 203, + 171 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "enabled", + "type": "bool" + } + ] + }, + { + "name": "toggle_mayhem_mode", + "discriminator": [ + 1, + 9, + 111, + 208, + 100, + 31, + 255, + 163 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "enabled", + "type": "bool" + } + ] + }, + { + "name": "update_buyback_config", + "discriminator": [ + 251, + 224, + 171, + 146, + 160, + 26, + 113, + 233 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "buyback_basis_points", + "type": { + "option": "u64" + } + } + ] + }, + { + "name": "update_creator_fee_config", + "discriminator": [ + 61, + 175, + 160, + 249, + 66, + 66, + 136, + 175 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" + } + ] + }, + { + "name": "update_global_authority", + "discriminator": [ + 227, + 181, + 74, + 196, + 208, + 21, + 97, + 213 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "signer": true, + "relations": [ + "global" + ] + }, + { + "name": "new_authority" + }, + { + "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": "update_holder_reward_config", + "discriminator": [ + 225, + 252, + 66, + 4, + 199, + 35, + 236, + 16 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "authority", + "writable": true, + "signer": true, + "relations": [ + "global" + ] + }, + { + "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": "is_holder_reward_enabled", + "type": "bool" + }, + { + "name": "holder_reward_claim_authority", + "type": "pubkey" + } + ] + } + ], + "accounts": [ + { + "name": "BondingCurve", + "discriminator": [ + 23, + 183, + 248, + 55, + 96, + 216, + 172, + 96 + ] + }, + { + "name": "FeeConfig", + "discriminator": [ + 143, + 52, + 146, + 187, + 219, + 123, + 76, + 155 + ] + }, + { + "name": "Global", + "discriminator": [ + 167, + 232, + 232, + 177, + 200, + 108, + 114, + 127 + ] + }, + { + "name": "GlobalVolumeAccumulator", + "discriminator": [ + 202, + 42, + 246, + 43, + 142, + 190, + 30, + 255 + ] + }, + { + "name": "QuoteControl", + "discriminator": [ + 56, + 244, + 35, + 238, + 193, + 213, + 162, + 201 + ] + }, + { + "name": "SharingConfig", + "discriminator": [ + 216, + 74, + 9, + 0, + 56, + 140, + 93, + 75 + ] + }, + { + "name": "UserVolumeAccumulator", + "discriminator": [ + 86, + 255, + 112, + 14, + 102, + 53, + 154, + 250 + ] + } + ], + "events": [ + { + "name": "AddQuoteControlMintEvent", + "discriminator": [ + 164, + 175, + 87, + 74, + 88, + 45, + 33, + 62 + ] + }, + { + "name": "AdminCtoEvent", + "discriminator": [ + 110, + 124, + 226, + 98, + 170, + 255, + 17, + 120 + ] + }, + { + "name": "AdminSetIdlAuthorityEvent", + "discriminator": [ + 245, + 59, + 70, + 34, + 75, + 185, + 109, + 92 + ] + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "discriminator": [ + 147, + 250, + 108, + 120, + 247, + 29, + 67, + 222 + ] + }, + { + "name": "ClaimCashbackEvent", + "discriminator": [ + 226, + 214, + 246, + 33, + 7, + 242, + 147, + 229 + ] + }, + { + "name": "ClaimTokenIncentivesEvent", + "discriminator": [ + 79, + 172, + 246, + 49, + 205, + 91, + 206, + 232 + ] + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "discriminator": [ + 146, + 159, + 189, + 172, + 146, + 88, + 56, + 244 + ] + }, + { + "name": "CollectCreatorFeeEvent", + "discriminator": [ + 122, + 2, + 127, + 1, + 14, + 191, + 12, + 175 + ] + }, + { + "name": "CompleteEvent", + "discriminator": [ + 95, + 114, + 97, + 156, + 212, + 46, + 152, + 8 + ] + }, + { + "name": "CompletePumpAmmMigrationEvent", + "discriminator": [ + 189, + 233, + 93, + 185, + 92, + 148, + 234, + 148 + ] + }, + { + "name": "CreateEvent", + "discriminator": [ + 27, + 114, + 169, + 77, + 222, + 235, + 99, + 118 + ] + }, + { + "name": "DistributeCreatorFeesEvent", + "discriminator": [ + 165, + 55, + 129, + 112, + 4, + 179, + 202, + 40 + ] + }, + { + "name": "DistributeFeeToHoldersEvent", + "discriminator": [ + 227, + 190, + 215, + 206, + 176, + 180, + 165, + 132 + ] + }, + { + "name": "ExtendAccountEvent", + "discriminator": [ + 97, + 97, + 215, + 144, + 93, + 146, + 22, + 124 + ] + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "discriminator": [ + 134, + 36, + 13, + 72, + 232, + 101, + 130, + 216 + ] + }, + { + "name": "MigrateBondingCurveCreatorEvent", + "discriminator": [ + 155, + 167, + 104, + 220, + 213, + 108, + 243, + 3 + ] + }, + { + "name": "MinimumDistributableFeeEvent", + "discriminator": [ + 168, + 216, + 132, + 239, + 235, + 182, + 49, + 52 + ] + }, + { + "name": "RemoveQuoteControlMintEvent", + "discriminator": [ + 46, + 33, + 86, + 134, + 0, + 213, + 209, + 48 + ] + }, + { + "name": "ReservedFeeRecipientsEvent", + "discriminator": [ + 43, + 188, + 250, + 18, + 221, + 75, + 187, + 95 + ] + }, + { + "name": "SetCreatorEvent", + "discriminator": [ + 237, + 52, + 123, + 37, + 245, + 251, + 72, + 210 + ] + }, + { + "name": "SetMetaplexCreatorEvent", + "discriminator": [ + 142, + 203, + 6, + 32, + 127, + 105, + 191, + 162 + ] + }, + { + "name": "SetParamsEvent", + "discriminator": [ + 223, + 195, + 159, + 246, + 62, + 48, + 143, + 131 + ] + }, + { + "name": "SetQuoteControlAdminEvent", + "discriminator": [ + 74, + 248, + 141, + 69, + 202, + 81, + 30, + 247 + ] + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "discriminator": [ + 197, + 122, + 167, + 124, + 116, + 81, + 91, + 255 + ] + }, + { + "name": "TradeEvent", + "discriminator": [ + 189, + 219, + 127, + 211, + 78, + 230, + 97, + 238 + ] + }, + { + "name": "UpdateCreatorFeeConfigEvent", + "discriminator": [ + 152, + 198, + 124, + 124, + 106, + 246, + 127, + 191 + ] + }, + { + "name": "UpdateGlobalAuthorityEvent", + "discriminator": [ + 182, + 195, + 137, + 42, + 35, + 206, + 207, + 247 + ] + }, + { + "name": "UpdateMayhemVirtualParamsEvent", + "discriminator": [ + 117, + 123, + 228, + 182, + 161, + 168, + 220, + 214 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "NotAuthorized", + "msg": "The given account is not authorized to execute this instruction." + }, + { + "code": 6001, + "name": "AlreadyInitialized", + "msg": "The program is already initialized." + }, + { + "code": 6002, + "name": "TooMuchSolRequired", + "msg": "slippage: Too much SOL required to buy the given amount of tokens." + }, + { + "code": 6003, + "name": "TooLittleSolReceived", + "msg": "slippage: Too little SOL received to sell the given amount of tokens." + }, + { + "code": 6004, + "name": "MintDoesNotMatchBondingCurve", + "msg": "The mint does not match the bonding curve." + }, + { + "code": 6005, + "name": "BondingCurveComplete", + "msg": "The bonding curve has completed and liquidity migrated to raydium." + }, + { + "code": 6006, + "name": "BondingCurveNotComplete", + "msg": "The bonding curve has not completed." + }, + { + "code": 6007, + "name": "NotInitialized", + "msg": "The program is not initialized." + }, + { + "code": 6008, + "name": "WithdrawTooFrequent", + "msg": "Withdraw too frequent" + }, + { + "code": 6009, + "name": "NewSizeShouldBeGreaterThanCurrentSize", + "msg": "new_size should be > current_size" + }, + { + "code": 6010, + "name": "AccountTypeNotSupported", + "msg": "Account type not supported" + }, + { + "code": 6011, + "name": "InitialRealTokenReservesShouldBeLessThanTokenTotalSupply", + "msg": "initial_real_token_reserves should be less than token_total_supply" + }, + { + "code": 6012, + "name": "InitialVirtualTokenReservesShouldBeGreaterThanInitialRealTokenReserves", + "msg": "initial_virtual_token_reserves should be greater than initial_real_token_reserves" + }, + { + "code": 6013, + "name": "FeeBasisPointsGreaterThanMaximum", + "msg": "fee_basis_points greater than maximum" + }, + { + "code": 6014, + "name": "AllZerosWithdrawAuthority", + "msg": "Withdraw authority cannot be set to System Program ID" + }, + { + "code": 6015, + "name": "PoolMigrationFeeShouldBeLessThanFinalRealSolReserves", + "msg": "pool_migration_fee should be less than final_real_sol_reserves" + }, + { + "code": 6016, + "name": "PoolMigrationFeeShouldBeGreaterThanCreatorFeePlusMaxMigrateFees", + "msg": "pool_migration_fee should be greater than creator_fee + MAX_MIGRATE_FEES" + }, + { + "code": 6017, + "name": "DisabledWithdraw", + "msg": "Migrate instruction is disabled" + }, + { + "code": 6018, + "name": "DisabledMigrate", + "msg": "Migrate instruction is disabled" + }, + { + "code": 6019, + "name": "InvalidCreator", + "msg": "Invalid creator pubkey" + }, + { + "code": 6020, + "name": "BuyZeroAmount", + "msg": "Buy zero amount" + }, + { + "code": 6021, + "name": "NotEnoughTokensToBuy", + "msg": "Not enough tokens to buy" + }, + { + "code": 6022, + "name": "SellZeroAmount", + "msg": "Sell zero amount" + }, + { + "code": 6023, + "name": "NotEnoughTokensToSell", + "msg": "Not enough tokens to sell" + }, + { + "code": 6024, + "name": "Overflow", + "msg": "Overflow" + }, + { + "code": 6025, + "name": "Truncation", + "msg": "Truncation" + }, + { + "code": 6026, + "name": "DivisionByZero", + "msg": "Division by zero" + }, + { + "code": 6027, + "name": "NotEnoughRemainingAccounts", + "msg": "Not enough remaining accounts" + }, + { + "code": 6028, + "name": "AllFeeRecipientsShouldBeNonZero", + "msg": "All fee recipients should be non-zero" + }, + { + "code": 6029, + "name": "UnsortedNotUniqueFeeRecipients", + "msg": "Unsorted or not unique fee recipients" + }, + { + "code": 6030, + "name": "CreatorShouldNotBeZero", + "msg": "Creator should not be zero" + }, + { + "code": 6031, + "name": "StartTimeInThePast" + }, + { + "code": 6032, + "name": "EndTimeInThePast" + }, + { + "code": 6033, + "name": "EndTimeBeforeStartTime" + }, + { + "code": 6034, + "name": "TimeRangeTooLarge" + }, + { + "code": 6035, + "name": "EndTimeBeforeCurrentDay" + }, + { + "code": 6036, + "name": "SupplyUpdateForFinishedRange" + }, + { + "code": 6037, + "name": "DayIndexAfterEndIndex" + }, + { + "code": 6038, + "name": "DayInActiveRange" + }, + { + "code": 6039, + "name": "InvalidIncentiveMint" + }, + { + "code": 6040, + "name": "BuyNotEnoughSolToCoverRent", + "msg": "Buy: Not enough SOL to cover for rent exemption." + }, + { + "code": 6041, + "name": "BuyNotEnoughSolToCoverFees", + "msg": "Buy: Not enough SOL to cover for fees." + }, + { + "code": 6042, + "name": "BuySlippageBelowMinTokensOut", + "msg": "Slippage: Would buy less tokens than expected min_tokens_out" + }, + { + "code": 6043, + "name": "NameTooLong" + }, + { + "code": 6044, + "name": "SymbolTooLong" + }, + { + "code": 6045, + "name": "UriTooLong" + }, + { + "code": 6046, + "name": "CreateV2Disabled" + }, + { + "code": 6047, + "name": "CpitializeMayhemFailed" + }, + { + "code": 6048, + "name": "MayhemModeDisabled" + }, + { + "code": 6049, + "name": "CreatorMigratedToSharingConfig", + "msg": "creator has been migrated to sharing config" + }, + { + "code": 6050, + "name": "UnableToDistributeCreatorVaultMigratedToSharingConfig", + "msg": "creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead" + }, + { + "code": 6051, + "name": "SharingConfigNotActive", + "msg": "Sharing config is not active" + }, + { + "code": 6052, + "name": "UnableToDistributeCreatorFeesToExecutableRecipient", + "msg": "The recipient account is executable, so it cannot receive lamports, remove it from the team first" + }, + { + "code": 6053, + "name": "BondingCurveAndSharingConfigCreatorMismatch", + "msg": "Bonding curve creator does not match sharing config" + }, + { + "code": 6054, + "name": "ShareholdersAndRemainingAccountsMismatch", + "msg": "Remaining accounts do not match shareholders, make sure to pass exactly the same pubkeys in the same order" + }, + { + "code": 6055, + "name": "InvalidShareBps", + "msg": "Share bps must be greater than 0" + }, + { + "code": 6056, + "name": "CashbackNotEnabled", + "msg": "Cashback is not enabled" + }, + { + "code": 6057, + "name": "BuybackFeeRecipientNotAuthorized", + "msg": "Buyback fee recipient not authorized" + }, + { + "code": 6058, + "name": "AllBuybackFeeRecipientsShouldBeNonZero" + }, + { + "code": 6059, + "name": "NotUniqueBuybackFeeRecipients" + }, + { + "code": 6060, + "name": "BuybackBasisPointsOutOfRange", + "msg": "buyback_basis_points must be <= 10_000" + }, + { + "code": 6061, + "name": "WrongBuybackFeeRecipientsCount", + "msg": "buyback fee recipients require exactly 8 remaining accounts (or none)" + }, + { + "code": 6062, + "name": "BuybackFeeRecipientMissing" + }, + { + "code": 6063, + "name": "UnsupportedQuoteMint", + "msg": "Unsupported quote mint" + }, + { + "code": 6064, + "name": "InvalidQuoteTokenProgram", + "msg": "Create v2: quote token program must be SPL Token or Token-2022" + }, + { + "code": 6065, + "name": "InvalidAssociatedQuoteBondingCurve", + "msg": "Create v2: associated quote bonding curve address does not match derivation" + }, + { + "code": 6066, + "name": "QuoteMintWhitelistFull", + "msg": "Quote mint whitelist is full" + }, + { + "code": 6067, + "name": "QuoteMintAlreadyWhitelisted", + "msg": "Quote mint is already whitelisted" + }, + { + "code": 6068, + "name": "QuoteMintNotWhitelisted", + "msg": "Quote mint is not in the whitelist" + }, + { + "code": 6069, + "name": "QuoteMintNotEligibleForWhitelist", + "msg": "Quote mint cannot be added or removed via whitelist (default or native SOL mint)" + }, + { + "code": 6070, + "name": "UnableToDistributeCreatorFeesToUninitializedAccount", + "msg": "Unable to distribute creator fees to uninitialized account" + }, + { + "code": 6071, + "name": "MayhemModeQuoteMintNotAllowed", + "msg": "Mayhem mode quote mint not allowed" + }, + { + "code": 6072, + "name": "MissingCashbackAccounts", + "msg": "Cashback trade is missing the required remaining accounts" + }, + { + "code": 6073, + "name": "InvalidCashbackAccumulator", + "msg": "Cashback user_volume_accumulator account is invalid" + }, + { + "code": 6074, + "name": "InvalidBondingCurveV2", + "msg": "bonding_curve_v2 remaining account is missing or invalid" + }, + { + "code": 6075, + "name": "InvalidQuoteControl", + "msg": "quote_control remaining account does not match derivation or is uninitialized" + }, + { + "code": 6076, + "name": "InvalidCashbackRecipient", + "msg": "Cashback recipient token account is not owned by user" + }, + { + "code": 6077, + "name": "CreatorFeeNotConfigurable", + "msg": "Configurable creator fees are disabled" + }, + { + "code": 6078, + "name": "CreatorFeeBpsOutOfRange", + "msg": "Creator fee basis points must be between 1 and the configured maximum" + }, + { + "code": 6079, + "name": "CreatorFeeNotEditable", + "msg": "Creator fee is not editable for this bonding curve" + }, + { + "code": 6080, + "name": "CreatorFeeNotAllowedForCashbackCoin", + "msg": "Creator fee cannot be configured for a cashback coin" + }, + { + "code": 6081, + "name": "BondingCurveAlreadyMigrated", + "msg": "Bonding curve has already migrated" + }, + { + "code": 6082, + "name": "CashbackDeprecated", + "msg": "Cashback coins can no longer be created" + }, + { + "code": 6083, + "name": "HolderRewardCreatorImmutable", + "msg": "The creator of a holder-reward coin cannot be changed" + }, + { + "code": 6084, + "name": "HolderRewardDisabled", + "msg": "Holder-reward coins are disabled" + }, + { + "code": 6085, + "name": "HolderRewardRecipientsMismatch", + "msg": "Holder-reward amounts and recipient accounts do not match" + }, + { + "code": 6086, + "name": "HolderRewardsRentFloor", + "msg": "The holder-rewards PDA cannot be left below its rent-exempt minimum" + }, + { + "code": 6087, + "name": "HolderRewardTokenAccountMissing", + "msg": "A holder-rewards token account is required on a token quote" + }, + { + "code": 6088, + "name": "CtoNotAllowedForMayhemCoin", + "msg": "CTO is not allowed on a mayhem-mode coin" + }, + { + "code": 6089, + "name": "CtoNewCreatorRequired", + "msg": "new_creator is required unless converting to holder rewards" + }, + { + "code": 6090, + "name": "CtoNewCreatorNotAllowed", + "msg": "new_creator must be omitted when converting to holder rewards" + }, + { + "code": 6091, + "name": "CreatorFeeNotConfigurableForQuote", + "msg": "Creator fee is not configurable on a SOL or whitelisted quote; the fee schedule applies" + }, + { + "code": 6092, + "name": "CtoCreatorAccountNotWritable", + "msg": "current_creator must be passed writable so the outgoing creator can be paid" + }, + { + "code": 6093, + "name": "CtoSharedVaultFrozen", + "msg": "A frozen sharing-config vault account holds a balance; thaw it before the holder conversion" + } + ], + "types": [ + { + "name": "AddQuoteControlMintEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "quote_control", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "AdminCtoEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "old_creator", + "type": "pubkey" + }, + { + "name": "new_creator", + "type": "pubkey" + }, + { + "name": "is_holder_reward", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "old_creator_fee_bps", + "type": "u64" + }, + { + "name": "new_creator_fee_bps", + "type": "u64" + }, + { + "name": "sharing_config_reset", + "type": "bool" + }, + { + "name": "swept_to_holder_vault", + "type": "u64" + }, + { + "name": "pool_updated", + "type": "bool" + } + ] + } + }, + { + "name": "AdminSetIdlAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "idl_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "AdminUpdateTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "day_number", + "type": "u64" + }, + { + "name": "token_supply_per_day", + "type": "u64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "BondingCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_quote_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_coin", + "type": "bool" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "creator_fee_bps", + "type": "u64" + }, + { + "name": "can_edit_creator_fee", + "type": "bool" + }, + { + "name": "is_holder_reward", + "type": "bool" + } + ] + } + }, + { + "name": "ClaimCashbackEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed", + "type": "u64" + }, + { + "name": "total_cashback_earned", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimTokenIncentivesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + } + ] + } + }, + { + "name": "CloseUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "CollectCreatorFeeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "creator_fee", + "type": "u64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "CompleteEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "CompletePumpAmmMigrationEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "mint_amount", + "type": "u64" + }, + { + "name": "sol_amount", + "docs": [ + "The variable is interpreted as `amount` in the offchain services", + "It is amount in terms of quoteMint for the bondingCurve", + "The rename is not done yet to avoid breaking changes in offchain services." + ], + "type": "u64" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "pool", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "ConfigStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Paused" + }, + { + "name": "Active" + } + ] + } + }, + { + "name": "CreateEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "token_program", + "type": "pubkey" + }, + { + "name": "is_mayhem_mode", + "type": "bool" + }, + { + "name": "is_cashback_enabled", + "type": "bool" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "creator_fee_bps", + "type": "u64" + }, + { + "name": "is_holder_reward", + "type": "bool" + } + ] + } + }, + { + "name": "DistributeCreatorFeesEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "distributed", + "type": "u64" + }, + { + "name": "quote_mint", + "type": "pubkey" + } + ] + } + }, + { + "name": "DistributeFeeToHoldersEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "recipients", + "type": "u64" + }, + { + "name": "total", + "type": "u64" + } + ] + } + }, + { + "name": "ExtendAccountEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "account", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "current_size", + "type": "u64" + }, + { + "name": "new_size", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "FeeConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + }, + { + "name": "fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "stable_fee_tiers", + "type": { + "vec": { + "defined": { + "name": "FeeTier" + } + } + } + }, + { + "name": "exotic_flat_fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "FeeTier", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market_cap_lamports_threshold", + "type": "u128" + }, + { + "name": "fees", + "type": { + "defined": { + "name": "Fees" + } + } + } + ] + } + }, + { + "name": "Fees", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lp_fee_bps", + "type": "u64" + }, + { + "name": "protocol_fee_bps", + "type": "u64" + }, + { + "name": "creator_fee_bps", + "type": "u64" + } + ] + } + }, + { + "name": "Global", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "docs": [ + "Unused" + ], + "type": "bool" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "docs": [ + "Unused" + ], + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + }, + { + "name": "create_v2_enabled", + "type": "bool" + }, + { + "name": "whitelist_pda", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "mayhem_mode_enabled", + "type": "bool" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + }, + { + "name": "is_cashback_enabled", + "type": "bool" + }, + { + "name": "buyback_fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "buyback_basis_points", + "type": "u64" + }, + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + }, + { + "name": "whitelisted_quote_mints", + "type": { + "array": [ + "pubkey", + 1 + ] + } + }, + { + "name": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" + }, + { + "name": "holder_reward_claim_authority", + "type": "pubkey" + }, + { + "name": "is_holder_reward_enabled", + "type": "bool" + } + ] + } + }, + { + "name": "GlobalVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_time", + "type": "i64" + }, + { + "name": "end_time", + "type": "i64" + }, + { + "name": "seconds_in_a_day", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "total_token_supply", + "type": { + "array": [ + "u64", + 30 + ] + } + }, + { + "name": "sol_volumes", + "type": { + "array": [ + "u64", + 30 + ] + } + } + ] + } + }, + { + "name": "InitUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "payer", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "MigrateBondingCurveCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "sharing_config", + "type": "pubkey" + }, + { + "name": "old_creator", + "type": "pubkey" + }, + { + "name": "new_creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "MinimumDistributableFeeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "minimum_required", + "type": "u64" + }, + { + "name": "distributable_fees", + "type": "u64" + }, + { + "name": "can_distribute", + "type": "bool" + } + ] + } + }, + { + "name": "OptionBool", + "type": { + "kind": "struct", + "fields": [ + "bool" + ] + } + }, + { + "name": "OptionU64", + "type": { + "kind": "struct", + "fields": [ + "u64" + ] + } + }, + { + "name": "QuoteControl", + "type": { + "kind": "struct", + "fields": [ + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "_reserved", + "type": { + "array": [ + "u8", + 64 + ] + } + }, + { + "name": "mints", + "type": { + "vec": { + "defined": { + "name": "QuoteControlMint" + } + } + } + } + ] + } + }, + { + "name": "QuoteControlMint", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "initial_virtual_quote_reserves", + "type": "u64" + } + ] + } + }, + { + "name": "RemoveQuoteControlMintEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "quote_control", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "ReservedFeeRecipientsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "reserved_fee_recipient", + "type": "pubkey" + }, + { + "name": "reserved_fee_recipients", + "type": { + "array": [ + "pubkey", + 7 + ] + } + } + ] + } + }, + { + "name": "SetCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetMetaplexCreatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "metadata", + "type": "pubkey" + }, + { + "name": "creator", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetParamsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "final_real_sol_reserves", + "type": "u64" + }, + { + "name": "token_total_supply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "withdraw_authority", + "type": "pubkey" + }, + { + "name": "enable_migrate", + "type": "bool" + }, + { + "name": "pool_migration_fee", + "type": "u64" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "fee_recipients", + "type": { + "array": [ + "pubkey", + 8 + ] + } + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "set_creator_authority", + "type": "pubkey" + }, + { + "name": "admin_set_creator_authority", + "type": "pubkey" + } + ] + } + }, + { + "name": "SetQuoteControlAdminEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "quote_control", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "old_admin", + "type": "pubkey" + }, + { + "name": "new_admin", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "Shareholder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "pubkey" + }, + { + "name": "share_bps", + "type": "u16" + } + ] + } + }, + { + "name": "SharingConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "status", + "type": { + "defined": { + "name": "ConfigStatus" + } + } + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "admin", + "type": "pubkey" + }, + { + "name": "admin_revoked", + "type": "bool" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + } + ] + } + }, + { + "name": "SyncUserVolumeAccumulatorEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "total_claimed_tokens_before", + "type": "u64" + }, + { + "name": "total_claimed_tokens_after", + "type": "u64" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "TradeEvent", + "docs": [ + "ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\"" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "sol_amount", + "type": "u64" + }, + { + "name": "token_amount", + "type": "u64" + }, + { + "name": "is_buy", + "type": "bool" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "fee_basis_points", + "type": "u64" + }, + { + "name": "fee", + "type": "u64" + }, + { + "name": "creator", + "type": "pubkey" + }, + { + "name": "creator_fee_basis_points", + "type": "u64" + }, + { + "name": "creator_fee", + "type": "u64" + }, + { + "name": "track_volume", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "ix_name", + "type": "string" + }, + { + "name": "mayhem_mode", + "type": "bool" + }, + { + "name": "cashback_fee_basis_points", + "type": "u64" + }, + { + "name": "cashback", + "type": "u64" + }, + { + "name": "buyback_fee_basis_points", + "type": "u64" + }, + { + "name": "buyback_fee", + "type": "u64" + }, + { + "name": "shareholders", + "type": { + "vec": { + "defined": { + "name": "Shareholder" + } + } + } + }, + { + "name": "quote_mint", + "type": "pubkey" + }, + { + "name": "quote_amount", + "type": "u64" + }, + { + "name": "virtual_quote_reserves", + "type": "u64" + }, + { + "name": "real_quote_reserves", + "type": "u64" + }, + { + "name": "holder_rewards_bps", + "type": "u64" + }, + { + "name": "holder_rewards", + "type": "u64" + } + ] + } + }, + { + "name": "UpdateCreatorFeeConfigEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "creator_fee_configurable", + "type": "bool" + }, + { + "name": "max_configurable_creator_fee_bps", + "type": "u64" + } + ] + } + }, + { + "name": "UpdateGlobalAuthorityEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "pubkey" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "new_authority", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "UpdateMayhemVirtualParamsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "new_virtual_token_reserves", + "type": "u64" + }, + { + "name": "new_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + } + ] + } + }, + { + "name": "UserVolumeAccumulator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "needs_claim", + "type": "bool" + }, + { + "name": "total_unclaimed_tokens", + "type": "u64" + }, + { + "name": "total_claimed_tokens", + "type": "u64" + }, + { + "name": "current_sol_volume", + "type": "u64" + }, + { + "name": "last_update_timestamp", + "type": "i64" + }, + { + "name": "has_total_claimed_tokens", + "type": "bool" + }, + { + "name": "cashback_earned", + "type": "u64" + }, + { + "name": "total_cashback_claimed", + "type": "u64" + }, + { + "name": "stable_cashback_earned", + "type": "u64" + }, + { + "name": "total_stable_cashback_claimed", + "type": "u64" + } + ] + } + } + ] } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index df0ce4a..f7c05db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pumpfun-cli" -version = "0.1.0" +version = "0.2.0" description = "CLI for pump.fun on Solana — trade, launch, and browse tokens" readme = "README.md" license = {text = "Apache-2.0"} @@ -8,11 +8,12 @@ requires-python = ">=3.12" dependencies = [ "typer", "httpx", - "solana", + "solana>=0.40.3", "cryptography", "python-dotenv", "tomli-w", "base58>=2.1.1", + "solders>=0.29.0", ] [project.scripts] diff --git a/scripts/mainnet-test.sh b/scripts/mainnet-test.sh index 55022f3..ccb959f 100755 --- a/scripts/mainnet-test.sh +++ b/scripts/mainnet-test.sh @@ -366,12 +366,7 @@ for t in tokens: # --- Group 6b: Token Launch --- echo "=== Group 6b: Token Launch ===" - # Generate a minimal test image - python3 -c "from PIL import Image; Image.new('RGB',(100,100),'blue').save('/tmp/e2e_test_token.png')" 2>/dev/null - LAUNCH_IMG="" - [[ -f /tmp/e2e_test_token.png ]] && LAUNCH_IMG="--image /tmp/e2e_test_token.png" - - LAUNCH_OUT=$(uv run pumpfun --json launch --name "E2E Test $(date +%s)" --ticker "E2ET" --desc "Automated e2e test token" $LAUNCH_IMG 2>&1) + LAUNCH_OUT=$(uv run pumpfun --json launch --name "E2E Test $(date +%s)" --ticker "E2ET" --uri https://example.com/token-v2.json 2>&1) LAUNCH_EXIT=$? echo "$LAUNCH_OUT" > "$LAST_OUTPUT_FILE" if [[ $LAUNCH_EXIT -eq 0 ]]; then @@ -391,7 +386,7 @@ for t in tokens: fi # Launch + buy - LAUNCH_BUY_OUT=$(uv run pumpfun --json launch --name "E2E Buy Test $(date +%s)" --ticker "E2EB" --desc "Automated e2e launch+buy" $LAUNCH_IMG --buy 0.001 2>&1) + LAUNCH_BUY_OUT=$(uv run pumpfun --json launch --name "E2E Buy Test $(date +%s)" --ticker "E2EB" --uri https://example.com/token-v2.json --buy 0.001 2>&1) LAUNCH_BUY_EXIT=$? echo "$LAUNCH_BUY_OUT" > "$LAST_OUTPUT_FILE" if [[ $LAUNCH_BUY_EXIT -eq 0 ]]; then diff --git a/src/pumpfun_cli/__init__.py b/src/pumpfun_cli/__init__.py index ffd6d9d..04fae4b 100644 --- a/src/pumpfun_cli/__init__.py +++ b/src/pumpfun_cli/__init__.py @@ -1,3 +1,3 @@ """pumpfun-cli — CLI for pump.fun on Solana.""" -__version__ = "0.1.0" +__version__ = "0.2.0" diff --git a/src/pumpfun_cli/commands/info.py b/src/pumpfun_cli/commands/info.py index 344d1db..d2d87bf 100644 --- a/src/pumpfun_cli/commands/info.py +++ b/src/pumpfun_cli/commands/info.py @@ -23,20 +23,24 @@ def info( state = ctx.obj if not state or not state.rpc: error("RPC endpoint not configured.", hint="Run: pumpfun config set rpc ") - result = asyncio.run(get_token_info(state.rpc, mint, rpc_timeout=rpc_timeout)) + try: + result = asyncio.run(get_token_info(state.rpc, mint, rpc_timeout=rpc_timeout)) + except (ValueError, RuntimeError) as exc: + error(str(exc)) if result.get("error"): error(result["message"]) if not render(result, state.json_mode): typer.echo(f"Mint: {result['mint']}") - typer.echo(f"Price: {result['price_sol']:.10f} SOL") + symbol = result["quote_symbol"] + typer.echo(f"Price: {result['price']:.10f} {symbol}") typer.echo(f"Bonding progress: {result['bonding_progress']}%") typer.echo(f"Graduated: {result['graduated']}") if result["graduated"] and result.get("pool_address"): typer.echo(f"Pool: {result['pool_address']}") typer.echo(f"Base reserves: {result['base_reserves']:.2f}") - typer.echo(f"Quote reserves: {result['quote_reserves_sol']:.4f} SOL") + typer.echo(f"Quote reserves: {result['quote_reserves']:.4f} {symbol}") else: - typer.echo(f"SOL reserves: {result['real_sol_reserves']:.2f} SOL") + typer.echo(f"Quote reserves: {result['real_quote_reserves']:.4f} {symbol}") if result.get("pumpswap_warning"): typer.echo(f"Warning: {result['pumpswap_warning']}") typer.echo(f"Creator: {result['creator']}") @@ -44,3 +48,7 @@ def info( typer.echo("Mayhem mode: Yes") if result["is_cashback_coin"]: typer.echo("Cashback: Yes") + if result.get("is_holder_reward"): + typer.echo("Holder rewards: Yes") + if result.get("creator_fee_bps"): + typer.echo(f"Creator fee: {result['creator_fee_bps']} bps") diff --git a/src/pumpfun_cli/commands/launch.py b/src/pumpfun_cli/commands/launch.py index 4379845..a1e960a 100644 --- a/src/pumpfun_cli/commands/launch.py +++ b/src/pumpfun_cli/commands/launch.py @@ -12,16 +12,47 @@ def launch( ctx: typer.Context, name: str = typer.Option(..., "--name", help="Token name"), ticker: str = typer.Option(..., "--ticker", help="Token ticker/symbol"), - desc: str = typer.Option(..., "--desc", help="Token description"), + desc: str = typer.Option("", "--desc", help="Token description (for the metadata upload)"), image: str | None = typer.Option(None, "--image", help="Path to token image"), - buy: float | None = typer.Option(None, "--buy", help="Initial buy amount in SOL"), - mayhem: bool = typer.Option(False, "--mayhem", help="Enable mayhem mode"), - cashback: bool = typer.Option(False, "--cashback", help="Enable cashback for the token"), + uri: str | None = typer.Option( + None, + "--uri", + help="Metadata JSON URI you already host; skips the pump.fun upload (--desc and --image are then unused)", + ), + buy: float | None = typer.Option( + None, + "--buy", + help="Opening buy, in the quote asset, bought in the same transaction as the create", + ), + quote_mint: str | None = typer.Option( + None, + "--quote-mint", + help="Price the coin in this mint instead of SOL (e.g. USDC); the wallet must hold it", + ), + creator_fee_bps: int | None = typer.Option( + None, + "--creator-fee-bps", + help="Creator fee in basis points; only applies to coins priced in a QuoteControl mint", + ), + holder_reward: bool = typer.Option( + False, "--holder-reward", help="Pay the creator fee to holders instead of the creator" + ), + mayhem: bool = typer.Option(False, "--mayhem", help="Enable mayhem mode (SOL coins only)"), + slippage: int = typer.Option( + 15, "--slippage", help="Slippage tolerance percent for the opening buy (default: 15)" + ), + confirm: bool = typer.Option(False, "--confirm", help="Wait for transaction confirmation"), ): """Launch a new token on pump.fun (create_v2 + extend_account).""" state = ctx.obj if not state or not state.rpc: error("RPC endpoint not configured.", hint="Run: pumpfun config set rpc ") + if slippage < 0 or slippage > 100: + error("Slippage must be between 0 and 100.", hint=f"Got: {slippage}") + if creator_fee_bps is not None and creator_fee_bps < 0: + error("Creator fee cannot be negative.", hint=f"Got: {creator_fee_bps}") + if buy is not None and buy < 0: + error("Buy amount cannot be negative.", hint=f"Got: {buy}") from pathlib import Path @@ -48,9 +79,14 @@ def launch( ticker, desc, image, - buy, - mayhem, - cashback, + initial_buy=buy, + is_mayhem=mayhem, + quote_mint_str=quote_mint, + creator_fee_bps=creator_fee_bps, + is_holder_reward=holder_reward, + slippage=slippage, + confirm=confirm, + uri=uri, **overrides, ) ) @@ -63,9 +99,12 @@ def launch( typer.echo(f" Name: {result['name']}") typer.echo(f" Ticker: {result['ticker']}") typer.echo(f" Mint: {result['mint']}") + typer.echo(f" Quote: {result['quote_symbol']}") typer.echo(f" TX: {result['explorer']}") typer.echo(f" Pump.fun: {result['pump_url']}") - if result.get("is_cashback"): - typer.echo(" Cashback: enabled") - if result.get("initial_buy_sol"): - typer.echo(f" Buy: {result['initial_buy_sol']} SOL") + if result.get("is_holder_reward"): + typer.echo(" Holder rewards: enabled") + if result.get("creator_fee_bps"): + typer.echo(f" Creator fee: {result['creator_fee_bps']} bps") + if result.get("initial_buy"): + typer.echo(f" Buy: {result['initial_buy']} {result['quote_symbol']}") diff --git a/src/pumpfun_cli/commands/trade.py b/src/pumpfun_cli/commands/trade.py index 40b4376..000297f 100644 --- a/src/pumpfun_cli/commands/trade.py +++ b/src/pumpfun_cli/commands/trade.py @@ -68,7 +68,9 @@ def _require_rpc_and_wallet(ctx: typer.Context) -> tuple: def buy( ctx: typer.Context, mint: str = typer.Argument(..., help="Token mint address"), - sol_amount: float = typer.Argument(..., help="Amount of SOL to spend"), + amount: float = typer.Argument( + ..., help="Amount to spend, in the coin's quote asset (SOL for most coins)" + ), slippage: int = typer.Option( 15, "--slippage", help="Slippage tolerance percent (default: 15)" ), @@ -80,8 +82,10 @@ def buy( False, "--dry-run", help="Simulate trade without sending transaction" ), ): - """Buy tokens with SOL.""" + """Buy tokens, spending an exact amount of the coin's quote asset.""" _validate_mint(mint) + if amount <= 0: + error("Amount must be greater than 0.", hint=f"Got: {amount}", exit_code=2) _validate_slippage(slippage) rpc, keyfile, password = _require_rpc_and_wallet(ctx) overrides = _get_overrides(ctx) @@ -93,7 +97,7 @@ def buy( keyfile, password, mint, - sol_amount, + amount, slippage, confirm=confirm, dry_run=dry_run, @@ -107,7 +111,7 @@ def buy( keyfile, password, mint, - sol_amount, + amount, slippage, confirm=confirm, dry_run=dry_run, @@ -121,14 +125,14 @@ def buy( keyfile, password, mint, - sol_amount, + amount, slippage, confirm=confirm, dry_run=dry_run, **overrides, ) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("dry_run"): state = ctx.obj @@ -136,10 +140,11 @@ def buy( typer.echo("Dry run — no transaction sent\n") typer.echo(f" Action: {result['action']}") typer.echo(f" Venue: {result['venue']}") - typer.echo(f" SOL in: {result['sol_in']}") + typer.echo(f" Spend: {result['quote_in']} {result['quote_symbol']}") typer.echo(f" Expected tokens: {result['expected_tokens']:.6f}") - typer.echo(f" Effective price: {result['effective_price_sol']:.10f} SOL/token") - typer.echo(f" Spot price: {result['spot_price_sol']:.10f} SOL/token") + unit = f"{result['quote_symbol']}/token" + typer.echo(f" Effective price: {result['effective_price']:.10f} {unit}") + typer.echo(f" Spot price: {result['spot_price']:.10f} {unit}") typer.echo(f" Price impact: {result['price_impact_pct']:.2f}%") typer.echo( f" Min tokens out: {result['min_tokens_out']:.6f} ({result['slippage_pct']}% slippage)" @@ -150,7 +155,10 @@ def buy( error(result["message"], exit_code=code) state = ctx.obj if not render(result, state.json_mode): - typer.echo(f"Bought {result['tokens_received']:.2f} tokens for {result['sol_spent']} SOL") + typer.echo( + f"Bought {result['tokens_received']:.2f} tokens" + f" for {result['quote_spent']} {result['quote_symbol']}" + ) typer.echo(f"TX: {result['explorer']}") @@ -169,7 +177,7 @@ def sell( False, "--dry-run", help="Simulate trade without sending transaction" ), ): - """Sell tokens for SOL.""" + """Sell tokens for the coin's quote asset (SOL for most coins).""" _validate_mint(mint) _validate_slippage(slippage) rpc, keyfile, password = _require_rpc_and_wallet(ctx) @@ -217,7 +225,7 @@ def sell( **overrides, ) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("dry_run"): state = ctx.obj @@ -226,12 +234,14 @@ def sell( typer.echo(f" Action: {result['action']}") typer.echo(f" Venue: {result['venue']}") typer.echo(f" Tokens in: {result['tokens_in']:.6f}") - typer.echo(f" Expected SOL: {result['expected_sol']:.6f}") - typer.echo(f" Effective price: {result['effective_price_sol']:.10f} SOL/token") - typer.echo(f" Spot price: {result['spot_price_sol']:.10f} SOL/token") + symbol = result["quote_symbol"] + typer.echo(f" Expected out: {result['expected_quote']:.6f} {symbol}") + typer.echo(f" Effective price: {result['effective_price']:.10f} {symbol}/token") + typer.echo(f" Spot price: {result['spot_price']:.10f} {symbol}/token") typer.echo(f" Price impact: {result['price_impact_pct']:.2f}%") typer.echo( - f" Min SOL out: {result['min_sol_out']:.6f} ({result['slippage_pct']}% slippage)" + f" Min out: {result['min_quote_out']:.6f} {symbol}" + f" ({result['slippage_pct']}% slippage)" ) return if result.get("error"): @@ -239,22 +249,29 @@ def sell( error(result["message"], exit_code=code) state = ctx.obj if not render(result, state.json_mode): - typer.echo(f"Sold {result['tokens_sold']:.2f} tokens for {result['sol_received']:.4f} SOL") + typer.echo( + f"Sold {result['tokens_sold']:.2f} tokens" + f" for {result['quote_received']:.6f} {result['quote_symbol']}" + ) typer.echo(f"TX: {result['explorer']}") +_QUOTE_MINT_HELP = "Quote asset mint to act on (default: SOL)" + + def claim_cashback( ctx: typer.Context, + quote_mint: str | None = typer.Option(None, "--quote-mint", help=_QUOTE_MINT_HELP), confirm: bool = typer.Option(False, "--confirm", help="Wait for transaction confirmation"), ): - """Claim volume-based cashback rewards.""" + """Claim volume-based cashback rewards in one quote asset.""" rpc, keyfile, password = _require_rpc_and_wallet(ctx) overrides = _get_overrides(ctx) try: result = asyncio.run( - claim_cashback_core(rpc, keyfile, password, confirm=confirm, **overrides) + claim_cashback_core(rpc, keyfile, password, quote_mint, confirm=confirm, **overrides) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("error"): error(result["message"]) @@ -275,7 +292,7 @@ def close_volume_acc( result = asyncio.run( close_volume_acc_core(rpc, keyfile, password, confirm=confirm, **overrides) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("error"): error(result["message"]) @@ -298,7 +315,7 @@ def migrate( result = asyncio.run( migrate_token(rpc, keyfile, password, mint, confirm=confirm, **overrides) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("error"): error(result["message"]) @@ -310,21 +327,25 @@ def migrate( def collect_creator_fee( ctx: typer.Context, + quote_mint: str | None = typer.Option(None, "--quote-mint", help=_QUOTE_MINT_HELP), confirm: bool = typer.Option(False, "--confirm", help="Wait for transaction confirmation"), ): - """Collect creator fees from pump.fun and PumpSwap vaults.""" + """Collect creator fees in one quote asset from pump.fun and PumpSwap vaults.""" rpc, keyfile, password = _require_rpc_and_wallet(ctx) overrides = _get_overrides(ctx) try: result = asyncio.run( - collect_creator_fees_core(rpc, keyfile, password, confirm=confirm, **overrides) + collect_creator_fees_core( + rpc, keyfile, password, quote_mint, confirm=confirm, **overrides + ) ) - except ValueError as exc: + except (ValueError, RuntimeError) as exc: error(str(exc)) if result.get("error"): error(result["message"]) state = ctx.obj if not render(result, state.json_mode): - typer.echo(f"Pump.fun fees: {result['pump_recovered_sol']:.6f} SOL") - typer.echo(f"PumpSwap fees: {result['pumpswap_recovered_sol']:.6f} SOL") + symbol = result["quote_symbol"] + typer.echo(f"Pump.fun fees: {result['pump_recovered']:.6f} {symbol}") + typer.echo(f"PumpSwap fees: {result['pumpswap_recovered']:.6f} {symbol}") typer.echo(f"TX: {result['explorer']}") diff --git a/src/pumpfun_cli/core/amounts.py b/src/pumpfun_cli/core/amounts.py new file mode 100644 index 0000000..685759d --- /dev/null +++ b/src/pumpfun_cli/core/amounts.py @@ -0,0 +1,13 @@ +"""Exact conversion of user-entered amounts into raw on-chain units.""" + +from decimal import ROUND_DOWN, Decimal + + +def to_raw(amount: float | str, decimals: int) -> int: + """Whole units to raw units, exact for the decimal text the user typed. + + Goes through the decimal string rather than a float product, which can + drop a raw unit: int(0.57 * 100) is 56. + """ + scaled = Decimal(str(amount)) * (Decimal(10) ** decimals) + return int(scaled.to_integral_value(ROUND_DOWN)) diff --git a/src/pumpfun_cli/core/info.py b/src/pumpfun_cli/core/info.py index 33e5309..6cea6f2 100644 --- a/src/pumpfun_cli/core/info.py +++ b/src/pumpfun_cli/core/info.py @@ -1,20 +1,16 @@ """Fetch on-chain token info.""" -from pathlib import Path - from pumpfun_cli.core.pumpswap import get_pumpswap_info from pumpfun_cli.core.validate import invalid_pubkey_error, parse_pubkey from pumpfun_cli.protocol.address import derive_bonding_curve from pumpfun_cli.protocol.client import RpcClient -from pumpfun_cli.protocol.contracts import LAMPORTS_PER_SOL from pumpfun_cli.protocol.curve import ( get_bonding_progress, - get_token_price_sol, + get_token_price, is_graduated, + parse_bonding_curve, ) -from pumpfun_cli.protocol.idl_parser import IDLParser - -IDL_PATH = Path(__file__).parent.parent.parent.parent / "idl" / "pump_fun_idl.json" +from pumpfun_cli.protocol.quote import fetch_global, get_mint_info, quote_symbol async def get_token_info(rpc_url: str, mint_str: str, rpc_timeout: float = 30.0) -> dict: @@ -23,37 +19,42 @@ async def get_token_info(rpc_url: str, mint_str: str, rpc_timeout: float = 30.0) return invalid_pubkey_error(mint_str, "mint address") bonding_curve = derive_bonding_curve(mint) client = RpcClient(rpc_url, timeout=rpc_timeout) - idl = IDLParser(str(IDL_PATH)) try: resp = await client.get_account_info(bonding_curve) if not resp.value: return {"error": "not_found", "message": f"No bonding curve found for {mint_str}"} - state = idl.decode_account_data(resp.value.data, "BondingCurve", skip_discriminator=True) - price = get_token_price_sol(state) - progress = get_bonding_progress(state) + state = parse_bonding_curve(bytes(resp.value.data)) + quote_mint = state["quote_mint"] + _, quote_decimals = await get_mint_info(client, quote_mint) + global_state = await fetch_global(client) graduated = is_graduated(state) + progress = get_bonding_progress(state, global_state["initial_real_token_reserves"]) result = { "mint": mint_str, "bonding_curve": str(bonding_curve), - "price_sol": price, + "quote_mint": str(quote_mint), + "quote_symbol": quote_symbol(quote_mint), + "price": get_token_price(state, quote_decimals), "bonding_progress": round(progress * 100, 1), "graduated": graduated, - "real_sol_reserves": state["real_sol_reserves"] / LAMPORTS_PER_SOL, - "virtual_sol_reserves": state["virtual_sol_reserves"] / LAMPORTS_PER_SOL, + "real_quote_reserves": state["real_quote_reserves"] / 10**quote_decimals, + "virtual_quote_reserves": state["virtual_quote_reserves"] / 10**quote_decimals, "virtual_token_reserves": state["virtual_token_reserves"], - "creator": str(state.get("creator", "")), - "is_mayhem_mode": state.get("is_mayhem_mode", False), - "is_cashback_coin": state.get("is_cashback_coin", False), + "creator": str(state["creator"]), + "is_mayhem_mode": state["is_mayhem_mode"], + "is_cashback_coin": state["is_cashback_coin"], + "is_holder_reward": bool(state["is_holder_reward"]), + "creator_fee_bps": state["creator_fee_bps"] or 0, } if graduated: amm_info = await get_pumpswap_info(rpc_url, mint_str, rpc_timeout=rpc_timeout) if not amm_info.get("error"): - result["price_sol"] = amm_info["price_sol"] + result["price"] = amm_info["price"] result["pool_address"] = amm_info["pool_address"] result["base_reserves"] = amm_info["base_reserves"] - result["quote_reserves_sol"] = amm_info["quote_reserves_sol"] + result["quote_reserves"] = amm_info["quote_reserves"] else: result["pumpswap_warning"] = f"PumpSwap pool lookup failed: {amm_info['message']}" result["pumpswap_error"] = amm_info["error"] diff --git a/src/pumpfun_cli/core/launch.py b/src/pumpfun_cli/core/launch.py index 62bae83..b3abac8 100644 --- a/src/pumpfun_cli/core/launch.py +++ b/src/pumpfun_cli/core/launch.py @@ -5,19 +5,38 @@ import httpx from solders.keypair import Keypair +from spl.token.instructions import create_idempotent_associated_token_account +from pumpfun_cli.core.amounts import to_raw +from pumpfun_cli.core.validate import parse_pubkey from pumpfun_cli.crypto import decrypt_keypair -from pumpfun_cli.protocol.address import derive_associated_bonding_curve, derive_bonding_curve -from pumpfun_cli.protocol.client import RpcClient -from pumpfun_cli.protocol.contracts import LAMPORTS_PER_SOL -from pumpfun_cli.protocol.idl_parser import IDLParser +from pumpfun_cli.protocol.address import derive_bonding_curve, find_ata, find_holder_rewards +from pumpfun_cli.protocol.client import RpcClient, TransactionFailedError +from pumpfun_cli.protocol.contracts import ( + HTTP_HEADERS, + LAUNCH_COMPUTE_UNITS, + TOKEN_2022_PROGRAM, + TOKEN_DECIMALS, + WSOL_MINT, +) from pumpfun_cli.protocol.instructions import ( - build_buy_instructions, - build_create_instructions, + build_buy_exact_quote_in_v2_instruction, + build_create_v2_instruction, build_extend_account_instruction, ) +from pumpfun_cli.protocol.quote import ( + check_creator_fee, + check_mayhem_quote, + fetch_global, + fetch_quote_control, + get_mint_info, + is_sol, + normalize_quote_mint, + opening_quote_reserves, + quote_symbol, +) -IDL_PATH = Path(__file__).parent.parent.parent.parent / "idl" / "pump_fun_idl.json" +_BPS = 10_000 async def upload_metadata( @@ -27,7 +46,7 @@ async def upload_metadata( image_path: str | None = None, ) -> str: """Upload token metadata to pump.fun's IPFS endpoint. Returns metadata URI.""" - async with httpx.AsyncClient(timeout=30.0) as client: + async with httpx.AsyncClient(timeout=30.0, headers=HTTP_HEADERS) as client: files = {} data = { "name": name, @@ -43,11 +62,32 @@ async def upload_metadata( files=files if files else None, ) if resp.status_code != 200: - raise RuntimeError(f"IPFS upload failed (HTTP {resp.status_code}): {resp.text[:200]}") + raise RuntimeError(f"Metadata upload to pump.fun failed (HTTP {resp.status_code})") result = resp.json() return result.get("metadataUri") or result.get("metadata_uri") or result.get("uri", "") +def opening_buy_tokens( + global_state: dict, amount_raw: int, opening_reserves: int, creator_fee_bps: int | None +) -> int: + """Tokens an opening buy of `amount_raw` quote units receives, after fees. + + Priced on the constant-product curve the coin opens with. + """ + creator_rate = ( + global_state.get("creator_fee_basis_points", 0) + if creator_fee_bps is None + else creator_fee_bps + ) + fee_bps = global_state.get("fee_basis_points", 0) + creator_rate + reaching_curve = amount_raw * (_BPS - fee_bps) // _BPS + return ( + reaching_curve + * global_state["initial_virtual_token_reserves"] + // (opening_reserves + reaching_curve) + ) + + async def launch_token( rpc_url: str, keystore_path: str, @@ -56,78 +96,165 @@ async def launch_token( ticker: str, description: str, image_path: str | None = None, - initial_buy_sol: float | None = None, + initial_buy: float | None = None, is_mayhem: bool = False, - is_cashback: bool = False, + quote_mint_str: str | None = None, + creator_fee_bps: int | None = None, + is_holder_reward: bool = False, + slippage: int = 15, + confirm: bool = False, priority_fee: int | None = None, compute_units: int | None = None, + uri: str | None = None, ) -> dict: - """Launch a new token on pump.fun using create_v2.""" + """Launch a new coin with create_v2, optionally buying it in the same transaction. + + `uri` is a metadata JSON already hosted elsewhere; without it the metadata + is uploaded to pump.fun, which may refuse an unauthenticated upload. + + With `initial_buy`, create and buy go out as one transaction (v1, since + together they exceed v0), so nobody can buy in between. + `initial_buy` is in the quote asset, which the wallet must already hold + when it is not SOL. + """ wallet = decrypt_keypair(Path(keystore_path), password) + user = wallet.pubkey() + quote_mint = WSOL_MINT + if quote_mint_str: + parsed = parse_pubkey(quote_mint_str, "quote mint") + if parsed is None: + return { + "error": "invalid_quote_mint", + "message": f"Invalid quote mint: {quote_mint_str}", + } + quote_mint = normalize_quote_mint(parsed) + client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - # 1. Upload metadata to IPFS - uri = await upload_metadata(name, ticker, description, image_path) + # Refuse anything the program would reject or silently ignore before + # uploading metadata or spending a fee. + check_mayhem_quote(quote_mint, is_mayhem) + global_state = await fetch_global(client) + if is_holder_reward and not global_state.get("is_holder_reward_enabled", False): + return { + "error": "holder_reward_disabled", + "message": "pump.fun is not accepting holder-reward coins right now.", + } + registry = {} + if not is_sol(quote_mint) or creator_fee_bps: + registry = await fetch_quote_control(client) + check_creator_fee(global_state, registry, quote_mint, creator_fee_bps) + opening_reserves = opening_quote_reserves(registry, quote_mint, global_state) + quote_program, quote_decimals = await get_mint_info(client, quote_mint) + symbol = quote_symbol(quote_mint) + + amount_raw = to_raw(initial_buy or 0, quote_decimals) + if amount_raw > 0 and not is_sol(quote_mint): + held = await client.get_token_account_balance( + find_ata(user, quote_mint, quote_program) + ) + if held < amount_raw: + return { + "error": "insufficient_balance", + "message": ( + f"Wallet holds {held / 10**quote_decimals} {symbol}" + f" but the opening buy spends {initial_buy} {symbol}." + ), + } + + if not uri: + try: + uri = await upload_metadata(name, ticker, description, image_path) + except (RuntimeError, httpx.HTTPError) as exc: + return { + "error": "ipfs_failed", + "message": f"{exc}. Host the metadata JSON yourself and pass --uri.", + } if not uri: return {"error": "ipfs_failed", "message": "Failed to get metadata URI from IPFS."} - # 2. Generate mint keypair mint_keypair = Keypair() mint = mint_keypair.pubkey() - - # 3. Build create_v2 instruction - instructions = build_create_instructions( - idl=idl, - mint=mint, - user=wallet.pubkey(), - name=name, - symbol=ticker, - uri=uri, - is_mayhem=is_mayhem, - is_cashback=is_cashback, - ) - - # 4. Add extend_account instruction (required for frontend visibility) bonding_curve = derive_bonding_curve(mint) - extend_ix = build_extend_account_instruction(idl, bonding_curve, wallet.pubkey()) - instructions.append(extend_ix) - - # 5. Optionally add initial buy - if initial_buy_sol and initial_buy_sol > 0: - sol_lamports = int(initial_buy_sol * LAMPORTS_PER_SOL) - assoc_bc = derive_associated_bonding_curve(mint, bonding_curve) - buy_ixs = build_buy_instructions( - idl=idl, + + instructions = [ + build_create_v2_instruction( mint=mint, - user=wallet.pubkey(), - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - creator=wallet.pubkey(), - is_mayhem=is_mayhem, - token_amount=1, # Minimum — contract handles actual amount - max_sol_cost=sol_lamports, + user=user, + creator=user, + name=name, + symbol=ticker, + uri=uri, + is_mayhem_mode=is_mayhem, + creator_fee_bps=creator_fee_bps, + is_holder_reward=True if is_holder_reward else None, + quote_mint=quote_mint, + quote_token_program=quote_program, + ), + build_extend_account_instruction(bonding_curve, user), + ] + + expected_tokens = 0 + if amount_raw > 0: + expected_tokens = opening_buy_tokens( + global_state, amount_raw, opening_reserves, creator_fee_bps ) - instructions.extend(buy_ixs) - - # 6. Send transaction (sign with both wallet and mint keypair) - sig = await client.send_tx( - instructions, - [wallet, mint_keypair], - compute_units=compute_units if compute_units is not None else 250_000, - priority_fee=priority_fee if priority_fee is not None else 500_000, - ) - return { + # A holder-reward coin's curve carries a PDA instead of the creator, + # and the buy's creator vault derives from what the curve carries. + curve_creator = find_holder_rewards(mint) if is_holder_reward else user + instructions += [ + create_idempotent_associated_token_account(user, user, mint, TOKEN_2022_PROGRAM), + build_buy_exact_quote_in_v2_instruction( + mint=mint, + creator=curve_creator, + user=user, + spendable_quote_in=amount_raw, + min_tokens_out=max(1, expected_tokens * (100 - slippage) // 100), + quote_mint=quote_mint, + base_token_program=TOKEN_2022_PROGRAM, + quote_token_program=quote_program, + is_mayhem_mode=is_mayhem, + ), + ] + + eff_compute_units = compute_units if compute_units is not None else LAUNCH_COMPUTE_UNITS + eff_priority_fee = priority_fee if priority_fee is not None else 500_000 + try: + # Create and buy together exceed a v0 transaction, so send_tx goes v1 + # and the coin is bought by the transaction that creates it. + sig = await client.send_tx( + instructions, + [wallet, mint_keypair], + compute_units=eff_compute_units, + priority_fee=eff_priority_fee, + confirm=confirm, + ) + except TransactionFailedError as exc: + return { + "error": "tx_error", + "message": f"Transaction failed on-chain: {exc.raw_error}", + "error_code": exc.error_code, + "mint": str(mint), + } + result = { "action": "launch", "name": name, "ticker": ticker, "mint": str(mint), "metadata_uri": uri, - "is_cashback": is_cashback, - "initial_buy_sol": initial_buy_sol, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, + "is_mayhem_mode": is_mayhem, + "is_holder_reward": is_holder_reward, + "creator_fee_bps": creator_fee_bps or 0, + "initial_buy": initial_buy, + "expected_tokens": expected_tokens / 10**TOKEN_DECIMALS, "signature": sig, "explorer": f"https://solscan.io/tx/{sig}", "pump_url": f"https://pump.fun/coin/{mint}", } + if confirm: + result["confirmed"] = True + return result finally: await client.close() diff --git a/src/pumpfun_cli/core/pumpswap.py b/src/pumpfun_cli/core/pumpswap.py index a9261a6..34af1f0 100644 --- a/src/pumpfun_cli/core/pumpswap.py +++ b/src/pumpfun_cli/core/pumpswap.py @@ -1,47 +1,53 @@ -"""PumpSwap AMM buy/sell execution and info.""" +"""PumpSwap AMM buy/sell execution and info. + +Trades go to the canonical pool a pump.fun curve graduated into, derived from +the mint and the curve's quote mint. Amounts are in that pool's quote asset. +""" from pathlib import Path -from spl.token.instructions import get_associated_token_address +from solders.pubkey import Pubkey +from pumpfun_cli.core.amounts import to_raw from pumpfun_cli.core.validate import invalid_pubkey_error, parse_pubkey from pumpfun_cli.crypto import decrypt_keypair -from pumpfun_cli.protocol.address import derive_amm_user_volume_accumulator +from pumpfun_cli.protocol.address import ( + derive_amm_user_volume_accumulator, + derive_bonding_curve, + find_ata, +) from pumpfun_cli.protocol.client import RpcClient, TransactionFailedError from pumpfun_cli.protocol.contracts import ( ATA_RENT_LAMPORTS, LAMPORTS_PER_SOL, - POOL_MAYHEM_MODE_MIN_SIZE, - POOL_MAYHEM_MODE_OFFSET, PUMPSWAP_BUY_COMPUTE_UNITS, PUMPSWAP_PRIORITY_FEE, PUMPSWAP_SELL_COMPUTE_UNITS, PUMPSWAP_SLIPPAGE_ERROR_CODES, SOL_RENT_EXEMPT_MIN, - TOKEN_DECIMALS, + WSOL_MINT, ) +from pumpfun_cli.protocol.curve import parse_bonding_curve from pumpfun_cli.protocol.instructions import ( build_init_amm_user_volume_accumulator, build_pumpswap_buy_exact_quote_in_instructions, build_pumpswap_sell_instructions, ) from pumpfun_cli.protocol.pumpswap import ( - get_fee_recipients, + get_global_config, get_pool_balances, get_pool_by_mint, - get_token_program_id, parse_pool_data, + pick_fee_recipients, + pool_price, + resolve_pool_tokens, ) +from pumpfun_cli.protocol.quote import is_sol, quote_symbol - -def _estimate_buy_required_lamports( - sol_lamports: int, - priority_fee: int, - compute_units: int, -) -> int: - """Estimate total lamports required for a PumpSwap buy transaction.""" - fee_lamports = priority_fee * compute_units // 1_000_000 - return sol_lamports + fee_lamports + ATA_RENT_LAMPORTS + SOL_RENT_EXEMPT_MIN +_BPS = 10_000 +# Headroom wrapped on top of a SOL buy, in percent. The throwaway WSOL account +# is closed in the same transaction, so whatever is not spent comes straight back. +_WSOL_FUNDING_BUFFER_PCT = 10 def _handle_tx_error(exc: TransactionFailedError, slippage_codes: set[int]) -> dict: @@ -59,12 +65,35 @@ def _handle_tx_error(exc: TransactionFailedError, slippage_codes: set[int]) -> d } +async def _load_pool(client: RpcClient, mint: Pubkey) -> tuple[Pubkey, dict, dict]: + """(pool address, parsed pool, token programs and decimals) for a graduated coin. + + The pool's quote mint is the curve's, so the curve is read first when it + still exists; a coin with no curve is looked up against SOL. + + Raises: + RuntimeError: If the pool does not exist + """ + quote_mint = WSOL_MINT + resp = await client.get_account_info(derive_bonding_curve(mint)) + if resp.value: + quote_mint = parse_bonding_curve(bytes(resp.value.data))["quote_mint"] + pool_address, pool_data = await get_pool_by_mint(client, mint, quote_mint) + pool = parse_pool_data(pool_data) + tokens = await resolve_pool_tokens(client, pool) + return pool_address, pool, tokens + + +def _total_fee_bps(config: dict) -> int: + return config["lp_fee_bps"] + config["protocol_fee_bps"] + config["coin_creator_fee_bps"] + + async def buy_pumpswap( rpc_url: str, keystore_path: str, password: str, mint_str: str, - sol_amount: float, + amount: float, slippage: int = 15, rpc_timeout: float = 30.0, confirm: bool = False, @@ -72,116 +101,116 @@ async def buy_pumpswap( compute_units: int | None = None, dry_run: bool = False, ) -> dict: - """Buy tokens via PumpSwap AMM.""" + """Spend exactly `amount` of the pool's quote asset on PumpSwap (buy_exact_quote_in).""" keypair = decrypt_keypair(Path(keystore_path), password) mint = parse_pubkey(mint_str, "mint address") if mint is None: return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url, timeout=rpc_timeout) try: - pool_address, pool_data = await get_pool_by_mint(client, mint) - pool = parse_pool_data(pool_data) - - token_program_id = await get_token_program_id(client, mint) - fee_recipient, fee_recipient_ata = await get_fee_recipients(client, pool_data) + pool_address, pool, tokens = await _load_pool(client, mint) + config = await get_global_config(client) base_balance, quote_balance = await get_pool_balances(client, pool) - if quote_balance <= 0 or base_balance <= 0: return {"error": "no_liquidity", "message": "Pool has no liquidity."} - sol_lamports = int(sol_amount * LAMPORTS_PER_SOL) - # Use buy_exact_quote_in: specify exact SOL to spend, minimum tokens to receive. - # This avoids u64 overflow in the program's buy path on high-liquidity pools. - spendable_quote_in = sol_lamports - # Estimate minimum tokens out using constant-product AMM formula with fee discount - effective_sol = sol_lamports * 99 // 100 # ~1% fee estimate - estimated_tokens = base_balance * effective_sol // (quote_balance + effective_sol) - min_base_amount_out = estimated_tokens * (100 - slippage) // 100 + quote_mint = pool["quote_mint"] + symbol = quote_symbol(quote_mint) + base_decimals = tokens["base_decimals"] + quote_decimals = tokens["quote_decimals"] + user = keypair.pubkey() + + amount_raw = to_raw(amount, quote_decimals) + effective_in = amount_raw * (_BPS - _total_fee_bps(config)) // _BPS + estimated_tokens = base_balance * effective_in // (quote_balance + effective_in) + min_base_amount_out = max(1, estimated_tokens * (100 - slippage) // 100) - # Pre-trade SOL balance validation eff_priority_fee = priority_fee if priority_fee is not None else PUMPSWAP_PRIORITY_FEE eff_compute_units = ( compute_units if compute_units is not None else PUMPSWAP_BUY_COMPUTE_UNITS ) - required_lamports = _estimate_buy_required_lamports( - sol_lamports, eff_priority_fee, eff_compute_units + wsol_funding = ( + amount_raw * (100 + _WSOL_FUNDING_BUFFER_PCT) // 100 if is_sol(quote_mint) else 0 ) - wallet_balance = await client.get_balance(keypair.pubkey()) + fee_lamports = eff_priority_fee * eff_compute_units // 1_000_000 + required_lamports = ( + wsol_funding + fee_lamports + 2 * ATA_RENT_LAMPORTS + SOL_RENT_EXEMPT_MIN + ) + wallet_balance = await client.get_balance(user) + shortfall = None + if wallet_balance < required_lamports: + shortfall = ( + f"Wallet has {wallet_balance / LAMPORTS_PER_SOL:.6f} SOL" + f" but trade requires ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL" + ) + elif not is_sol(quote_mint): + held = await client.get_token_account_balance( + find_ata(user, quote_mint, tokens["quote_token_program"]) + ) + if held < amount_raw: + shortfall = ( + f"Wallet holds {held / 10**quote_decimals} {symbol}" + f" but the buy spends {amount} {symbol}" + ) if dry_run: - spot_price = quote_balance / base_balance if base_balance > 0 else 0 - expected_tokens_human = estimated_tokens / (10**TOKEN_DECIMALS) - effective_price = ( - sol_amount / expected_tokens_human if expected_tokens_human > 0 else 0 - ) - spot_price_sol = spot_price * (10**TOKEN_DECIMALS) / LAMPORTS_PER_SOL + spot_price = pool_price(base_balance, quote_balance, base_decimals, quote_decimals) + expected_tokens = estimated_tokens / 10**base_decimals + effective_price = amount / expected_tokens if expected_tokens > 0 else 0 price_impact = ( - (effective_price - spot_price_sol) / spot_price_sol * 100 - if spot_price_sol > 0 - else 0 + (effective_price - spot_price) / spot_price * 100 if spot_price > 0 else 0 ) result = { "dry_run": True, "action": "buy", "venue": "pumpswap", "mint": mint_str, - "sol_in": sol_amount, - "expected_tokens": expected_tokens_human, - "effective_price_sol": effective_price, - "spot_price_sol": spot_price_sol, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, + "quote_in": amount, + "expected_tokens": expected_tokens, + "effective_price": effective_price, + "spot_price": spot_price, "price_impact_pct": round(price_impact, 4), - "min_tokens_out": min_base_amount_out / (10**TOKEN_DECIMALS), + "min_tokens_out": min_base_amount_out / 10**base_decimals, "slippage_pct": slippage, } - if wallet_balance < required_lamports: - result["balance_warning"] = ( - f"Wallet has {wallet_balance / LAMPORTS_PER_SOL:.6f} SOL" - f" but trade requires ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL" - ) + if shortfall: + result["balance_warning"] = shortfall return result - if wallet_balance < required_lamports: + if shortfall: return { "error": "insufficient_balance", - "message": ( - f"Insufficient SOL balance. Have {wallet_balance / LAMPORTS_PER_SOL:.6f}" - f" SOL, need ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL." - ), + "message": f"Insufficient balance. {shortfall}.", "available_sol": wallet_balance / LAMPORTS_PER_SOL, "required_sol": required_lamports / LAMPORTS_PER_SOL, } - # Wrap enough SOL to cover the spend plus a small buffer - wrap_lamports = int(sol_amount * 1.1 * LAMPORTS_PER_SOL) - if wrap_lamports < spendable_quote_in: - wrap_lamports = spendable_quote_in - + fee_recipient, buyback_recipient = pick_fee_recipients(config, pool["is_mayhem_mode"]) ixs = build_pumpswap_buy_exact_quote_in_instructions( - user=keypair.pubkey(), + user=user, pool_address=pool_address, pool=pool, - token_program_id=token_program_id, + tokens=tokens, fee_recipient=fee_recipient, - fee_recipient_ata=fee_recipient_ata, - spendable_quote_in=spendable_quote_in, + buyback_recipient=buyback_recipient, + spendable_quote_in=amount_raw, min_base_amount_out=min_base_amount_out, - sol_wrap_lamports=wrap_lamports, + wsol_funding=wsol_funding, ) - # Init user volume accumulator if it doesn't exist - user_vol = derive_amm_user_volume_accumulator(keypair.pubkey()) + user_vol = derive_amm_user_volume_accumulator(user) vol_resp = await client.get_account_info(user_vol) if not vol_resp.value: - ixs.insert(0, build_init_amm_user_volume_accumulator(keypair.pubkey())) + ixs.insert(0, build_init_amm_user_volume_accumulator(user)) try: sig = await client.send_tx( ixs, [keypair], - compute_units=compute_units - if compute_units is not None - else PUMPSWAP_BUY_COMPUTE_UNITS, - priority_fee=priority_fee if priority_fee is not None else PUMPSWAP_PRIORITY_FEE, + compute_units=eff_compute_units, + priority_fee=eff_priority_fee, confirm=confirm, ) except TransactionFailedError as exc: @@ -190,15 +219,17 @@ async def buy_pumpswap( "action": "buy", "venue": "pumpswap", "mint": mint_str, - "sol_spent": sol_amount, - "tokens_received": estimated_tokens / (10**TOKEN_DECIMALS), + "quote_mint": str(quote_mint), + "quote_symbol": symbol, + "quote_spent": amount, + "tokens_received": estimated_tokens / 10**base_decimals, "signature": sig, "explorer": f"https://solscan.io/tx/{sig}", } if confirm: result["confirmed"] = True return result - except RuntimeError as exc: + except (RuntimeError, ValueError) as exc: return {"error": "pumpswap_error", "message": str(exc)} finally: await client.close() @@ -217,85 +248,83 @@ async def sell_pumpswap( compute_units: int | None = None, dry_run: bool = False, ) -> dict: - """Sell tokens via PumpSwap AMM.""" + """Sell tokens on PumpSwap for the pool's quote asset.""" keypair = decrypt_keypair(Path(keystore_path), password) mint = parse_pubkey(mint_str, "mint address") if mint is None: return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url, timeout=rpc_timeout) try: - pool_address, pool_data = await get_pool_by_mint(client, mint) - pool = parse_pool_data(pool_data) - - token_program_id = await get_token_program_id(client, mint) - fee_recipient, fee_recipient_ata = await get_fee_recipients(client, pool_data) - - user_token_ata = get_associated_token_address( - keypair.pubkey(), - mint, - token_program_id, - ) + pool_address, pool, tokens = await _load_pool(client, mint) + quote_mint = pool["quote_mint"] + symbol = quote_symbol(quote_mint) + base_decimals = tokens["base_decimals"] + quote_decimals = tokens["quote_decimals"] + user = keypair.pubkey() + user_token_ata = find_ata(user, mint, tokens["base_token_program"]) if amount_str.lower() == "all": token_amount = await client.get_token_account_balance(user_token_ata) else: - token_amount = int(float(amount_str) * (10**TOKEN_DECIMALS)) - # Pre-trade token balance validation for specific amounts + token_amount = to_raw(amount_str, base_decimals) actual_balance = await client.get_token_account_balance(user_token_ata) if actual_balance < token_amount: return { "error": "insufficient_balance", "message": ( f"Insufficient token balance. Have" - f" {actual_balance / (10**TOKEN_DECIMALS):.6f} tokens," - f" need {token_amount / (10**TOKEN_DECIMALS):.6f} tokens." + f" {actual_balance / 10**base_decimals:.6f} tokens," + f" need {token_amount / 10**base_decimals:.6f} tokens." ), - "available_tokens": actual_balance / (10**TOKEN_DECIMALS), - "required_tokens": token_amount / (10**TOKEN_DECIMALS), + "available_tokens": actual_balance / 10**base_decimals, + "required_tokens": token_amount / 10**base_decimals, } if token_amount <= 0: return {"error": "no_tokens", "message": "No tokens to sell."} + config = await get_global_config(client) base_balance, quote_balance = await get_pool_balances(client, pool) - # Integer arithmetic: expected_sol_lamports = token_amount * quote_balance / base_balance - expected_sol_lamports = token_amount * quote_balance // base_balance - min_sol_lamports = expected_sol_lamports * (100 - slippage) // 100 + if base_balance <= 0 or quote_balance <= 0: + return {"error": "no_liquidity", "message": "Pool has no liquidity."} + gross_out = token_amount * quote_balance // (base_balance + token_amount) + expected_quote = gross_out * (_BPS - _total_fee_bps(config)) // _BPS + min_quote_out = max(1, expected_quote * (100 - slippage) // 100) if dry_run: - spot_price = quote_balance / base_balance if base_balance > 0 else 0 - tokens_human = token_amount / (10**TOKEN_DECIMALS) - sol_out_human = expected_sol_lamports / LAMPORTS_PER_SOL - effective_price = sol_out_human / tokens_human if tokens_human > 0 else 0 - spot_price_sol = spot_price * (10**TOKEN_DECIMALS) / LAMPORTS_PER_SOL + spot_price = pool_price(base_balance, quote_balance, base_decimals, quote_decimals) + tokens_human = token_amount / 10**base_decimals + quote_out_human = expected_quote / 10**quote_decimals + effective_price = quote_out_human / tokens_human if tokens_human > 0 else 0 price_impact = ( - (effective_price - spot_price_sol) / spot_price_sol * 100 - if spot_price_sol > 0 - else 0 + (effective_price - spot_price) / spot_price * 100 if spot_price > 0 else 0 ) return { "dry_run": True, "action": "sell", "venue": "pumpswap", "mint": mint_str, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, "tokens_in": tokens_human, - "expected_sol": sol_out_human, - "effective_price_sol": effective_price, - "spot_price_sol": spot_price_sol, + "expected_quote": quote_out_human, + "effective_price": effective_price, + "spot_price": spot_price, "price_impact_pct": round(price_impact, 4), - "min_sol_out": min_sol_lamports / LAMPORTS_PER_SOL, + "min_quote_out": min_quote_out / 10**quote_decimals, "slippage_pct": slippage, } + fee_recipient, buyback_recipient = pick_fee_recipients(config, pool["is_mayhem_mode"]) ixs = build_pumpswap_sell_instructions( - user=keypair.pubkey(), + user=user, pool_address=pool_address, pool=pool, - token_program_id=token_program_id, + tokens=tokens, fee_recipient=fee_recipient, - fee_recipient_ata=fee_recipient_ata, + buyback_recipient=buyback_recipient, token_amount=token_amount, - min_sol_out=min_sol_lamports, + min_quote_out=min_quote_out, ) try: @@ -314,15 +343,17 @@ async def sell_pumpswap( "action": "sell", "venue": "pumpswap", "mint": mint_str, - "tokens_sold": token_amount / (10**TOKEN_DECIMALS), - "sol_received": expected_sol_lamports / LAMPORTS_PER_SOL, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, + "tokens_sold": token_amount / 10**base_decimals, + "quote_received": expected_quote / 10**quote_decimals, "signature": sig, "explorer": f"https://solscan.io/tx/{sig}", } if confirm: result["confirmed"] = True return result - except RuntimeError as exc: + except (RuntimeError, ValueError) as exc: return {"error": "pumpswap_error", "message": str(exc)} finally: await client.close() @@ -335,25 +366,22 @@ async def get_pumpswap_info(rpc_url: str, mint_str: str, rpc_timeout: float = 30 return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url, timeout=rpc_timeout) try: - pool_address, pool_data = await get_pool_by_mint(client, mint) - pool = parse_pool_data(pool_data) - + pool_address, pool, tokens = await _load_pool(client, mint) base_balance, quote_balance = await get_pool_balances(client, pool) - price = quote_balance / base_balance if base_balance > 0 else 0.0 - - is_mayhem = len(pool_data) >= POOL_MAYHEM_MODE_MIN_SIZE and bool( - pool_data[POOL_MAYHEM_MODE_OFFSET] - ) - + base_decimals = tokens["base_decimals"] + quote_decimals = tokens["quote_decimals"] return { "pool_address": str(pool_address), - "price_sol": price, - "base_reserves": base_balance / (10**TOKEN_DECIMALS), - "quote_reserves_sol": quote_balance / LAMPORTS_PER_SOL, + "quote_mint": str(pool["quote_mint"]), + "quote_symbol": quote_symbol(pool["quote_mint"]), + "price": pool_price(base_balance, quote_balance, base_decimals, quote_decimals), + "base_reserves": base_balance / 10**base_decimals, + "quote_reserves": quote_balance / 10**quote_decimals, "creator": str(pool["coin_creator"]), - "mayhem_mode": is_mayhem, + "mayhem_mode": pool["is_mayhem_mode"], + "cashback": pool["is_cashback_coin"], } - except RuntimeError as exc: + except (RuntimeError, ValueError) as exc: return {"error": "pumpswap_error", "message": str(exc)} finally: await client.close() diff --git a/src/pumpfun_cli/core/tokens.py b/src/pumpfun_cli/core/tokens.py index d45765a..3d1f0cf 100644 --- a/src/pumpfun_cli/core/tokens.py +++ b/src/pumpfun_cli/core/tokens.py @@ -2,10 +2,32 @@ import httpx -from pumpfun_cli.protocol.curve import get_bonding_progress, get_token_price_sol +from pumpfun_cli.protocol.contracts import HTTP_HEADERS PUMP_API = "https://frontend-api-v3.pump.fun" +# The frontend API reports SOL-named reserves; these read them as the API returns them. +_LAMPORTS_PER_SOL = 1_000_000_000 +_TOKEN_DECIMALS = 6 +_GRADUATION_SOL = 85.0 + + +def get_token_price_sol(token: dict) -> float: + """Price in SOL per whole token from the API's virtual reserves.""" + if token["virtual_token_reserves"] == 0: + return 0.0 + return ( + token["virtual_sol_reserves"] + / token["virtual_token_reserves"] + * 10**_TOKEN_DECIMALS + / _LAMPORTS_PER_SOL + ) + + +def get_bonding_progress(token: dict) -> float: + """Approximate completion 0.0 – 1.0 from the API's real SOL reserves.""" + return min(token["real_sol_reserves"] / _LAMPORTS_PER_SOL / _GRADUATION_SOL, 1.0) + def _enrich_tokens(tokens: list[dict]) -> list[dict]: """Add price_sol and bonding_progress to each token dict.""" @@ -22,7 +44,7 @@ def _enrich_tokens(tokens: list[dict]) -> list[dict]: async def get_trending_tokens(limit: int = 20) -> list[dict]: - async with httpx.AsyncClient(timeout=10.0) as client: + async with httpx.AsyncClient(timeout=10.0, headers=HTTP_HEADERS) as client: resp = await client.get(f"{PUMP_API}/coins/top-runners") resp.raise_for_status() runners = resp.json() @@ -41,7 +63,7 @@ async def get_trending_tokens(limit: int = 20) -> list[dict]: async def get_recommended_tokens(limit: int = 50) -> list[dict]: - async with httpx.AsyncClient(timeout=10.0) as client: + async with httpx.AsyncClient(timeout=10.0, headers=HTTP_HEADERS) as client: resp = await client.get( f"{PUMP_API}/coins/recommended", params={"limit": min(limit, 300), "includeNsfw": "false"}, diff --git a/src/pumpfun_cli/core/trade.py b/src/pumpfun_cli/core/trade.py index c78c4b7..0a6a6f8 100644 --- a/src/pumpfun_cli/core/trade.py +++ b/src/pumpfun_cli/core/trade.py @@ -1,48 +1,62 @@ -"""Buy and sell execution.""" +"""Bonding-curve trades, payouts and migration. + +Amounts are in the coin's quote asset: SOL for most coins, or whatever mint +the curve records (USDC and others). Every result names that asset in +`quote_mint` / `quote_symbol`. +""" from pathlib import Path from solders.pubkey import Pubkey +from spl.token.instructions import create_idempotent_associated_token_account +from pumpfun_cli.core.amounts import to_raw from pumpfun_cli.core.validate import invalid_pubkey_error, parse_pubkey from pumpfun_cli.crypto import decrypt_keypair from pumpfun_cli.protocol.address import ( - derive_associated_bonding_curve, + derive_amm_creator_vault, derive_bonding_curve, + derive_creator_vault, + find_ata, + find_user_volume_accumulator, ) from pumpfun_cli.protocol.client import RpcClient, TransactionFailedError from pumpfun_cli.protocol.contracts import ( ATA_RENT_LAMPORTS, LAMPORTS_PER_SOL, + PUMP_BUY_COMPUTE_UNITS, + PUMP_NON_SOL_TRADE_COMPUTE_UNITS, + PUMP_SELL_COMPUTE_UNITS, PUMP_SLIPPAGE_ERROR_CODES, SOL_RENT_EXEMPT_MIN, TOKEN_DECIMALS, + WSOL_MINT, ) from pumpfun_cli.protocol.curve import ( calculate_buy_tokens_out, - calculate_sell_sol_out, - get_token_price_sol, + calculate_sell_quote_out, + get_token_price, + parse_bonding_curve, ) -from pumpfun_cli.protocol.idl_parser import IDLParser from pumpfun_cli.protocol.instructions import ( - build_buy_exact_sol_in_instructions, - build_claim_cashback_instruction, + build_buy_exact_quote_in_v2_instruction, + build_claim_cashback_v2_instruction, build_close_volume_accumulator_instruction, - build_migrate_instruction, - build_sell_instructions, + build_collect_coin_creator_fee_instruction, + build_collect_creator_fee_v2_instruction, + build_migrate_v2_instruction, + build_sell_v2_instruction, ) from pumpfun_cli.protocol.pumpswap import get_token_program_id +from pumpfun_cli.protocol.quote import ( + fetch_global, + get_mint_info, + is_sol, + normalize_quote_mint, + quote_symbol, +) -IDL_PATH = Path(__file__).parent.parent.parent.parent / "idl" / "pump_fun_idl.json" - - -def _coerce_pubkey(raw) -> Pubkey: - """Coerce raw IDL pubkey value (bytes/list/str/Pubkey) to Pubkey.""" - if isinstance(raw, (list, bytes)): - return Pubkey.from_bytes(bytes(raw)) - if isinstance(raw, str): - return Pubkey.from_string(raw) - return raw +DEFAULT_PRIORITY_FEE = 200_000 def _estimate_buy_required_lamports( @@ -70,55 +84,108 @@ def _handle_tx_error(exc: TransactionFailedError, slippage_codes: set[int]) -> d } +def _tx_result(action: str, sig: str, confirm: bool, **fields) -> dict: + result = { + "action": action, + **fields, + "signature": sig, + "explorer": f"https://solscan.io/tx/{sig}", + } + if confirm: + result["confirmed"] = True + return result + + +async def _read_curve(client: RpcClient, mint: Pubkey) -> dict | None: + """The coin's decoded bonding curve, or None if it has none.""" + resp = await client.get_account_info(derive_bonding_curve(mint)) + if not resp.value: + return None + return parse_bonding_curve(bytes(resp.value.data)) + + +async def _resolve_quote_arg( + client: RpcClient, quote_mint_str: str | None +) -> tuple[Pubkey, Pubkey]: + """(quote mint, its token program) for an optional --quote-mint flag; SOL by default.""" + quote_mint = WSOL_MINT + if quote_mint_str: + parsed = parse_pubkey(quote_mint_str, "quote mint") + if parsed is None: + raise ValueError(f"Invalid quote mint: {quote_mint_str}") + quote_mint = normalize_quote_mint(parsed) + quote_program, _ = await get_mint_info(client, quote_mint) + return quote_mint, quote_program + + async def buy_token( rpc_url: str, keystore_path: str, password: str, mint_str: str, - sol_amount: float, + amount: float, slippage: int = 15, confirm: bool = False, priority_fee: int | None = None, compute_units: int | None = None, dry_run: bool = False, ) -> dict: + """Spend exactly `amount` of the coin's quote asset on its bonding curve (buy_exact_quote_in_v2).""" keypair = decrypt_keypair(Path(keystore_path), password) mint = parse_pubkey(mint_str, "mint address") if mint is None: return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - bonding_curve = derive_bonding_curve(mint) - token_program = await get_token_program_id(client, mint) - assoc_bc = derive_associated_bonding_curve(mint, bonding_curve, token_program) - - resp = await client.get_account_info(bonding_curve) - if not resp.value: + state = await _read_curve(client, mint) + if state is None: return {"error": "not_found", "message": f"No bonding curve for {mint_str}"} - state = idl.decode_account_data(resp.value.data, "BondingCurve", skip_discriminator=True) - - if state.get("complete"): + if state["complete"]: return {"error": "graduated", "message": "Token has graduated to PumpSwap."} - sol_lamports = int(sol_amount * LAMPORTS_PER_SOL) - tokens_out = calculate_buy_tokens_out(state, sol_lamports) - min_tokens_out = int(tokens_out * (100 - slippage) / 100) + quote_mint = state["quote_mint"] + symbol = quote_symbol(quote_mint) + quote_program, quote_decimals = await get_mint_info(client, quote_mint) + base_program = await get_token_program_id(client, mint) + user = keypair.pubkey() - # Pre-trade SOL balance validation - eff_priority_fee = priority_fee if priority_fee is not None else 200_000 - eff_compute_units = compute_units if compute_units is not None else 100_000 + amount_raw = to_raw(amount, quote_decimals) + tokens_out = calculate_buy_tokens_out(state, amount_raw) + # The program rejects a zero floor (6020), so 100% slippage still asks for one unit. + min_tokens_out = max(1, int(tokens_out * (100 - slippage) / 100)) + + eff_priority_fee = priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE + default_units = ( + PUMP_BUY_COMPUTE_UNITS if is_sol(quote_mint) else PUMP_NON_SOL_TRADE_COMPUTE_UNITS + ) + eff_compute_units = compute_units if compute_units is not None else default_units + sol_spend = amount_raw if is_sol(quote_mint) else 0 required_lamports = _estimate_buy_required_lamports( - sol_lamports, eff_priority_fee, eff_compute_units + sol_spend, eff_priority_fee, eff_compute_units ) - wallet_balance = await client.get_balance(keypair.pubkey()) + wallet_balance = await client.get_balance(user) + quote_balance = None + if not is_sol(quote_mint): + quote_balance = await client.get_token_account_balance( + find_ata(user, quote_mint, quote_program) + ) - if dry_run: - spot_price = get_token_price_sol(state) - expected_tokens_human = tokens_out / (10**TOKEN_DECIMALS) - effective_price = ( - sol_amount / expected_tokens_human if expected_tokens_human > 0 else 0 + shortfall = None + if wallet_balance < required_lamports: + shortfall = ( + f"Wallet has {wallet_balance / LAMPORTS_PER_SOL:.6f} SOL" + f" but trade requires ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL" + ) + elif quote_balance is not None and quote_balance < amount_raw: + shortfall = ( + f"Wallet holds {quote_balance / 10**quote_decimals} {symbol}" + f" but the buy spends {amount} {symbol}" ) + + if dry_run: + spot_price = get_token_price(state, quote_decimals) + expected_tokens = tokens_out / 10**TOKEN_DECIMALS + effective_price = amount / expected_tokens if expected_tokens > 0 else 0 price_impact = ( (effective_price - spot_price) / spot_price * 100 if spot_price > 0 else 0 ) @@ -127,46 +194,41 @@ async def buy_token( "action": "buy", "venue": "bonding_curve", "mint": mint_str, - "sol_in": sol_amount, - "expected_tokens": expected_tokens_human, - "effective_price_sol": effective_price, - "spot_price_sol": spot_price, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, + "quote_in": amount, + "expected_tokens": expected_tokens, + "effective_price": effective_price, + "spot_price": spot_price, "price_impact_pct": round(price_impact, 4), - "min_tokens_out": min_tokens_out / (10**TOKEN_DECIMALS), + "min_tokens_out": min_tokens_out / 10**TOKEN_DECIMALS, "slippage_pct": slippage, } - if wallet_balance < required_lamports: - result["balance_warning"] = ( - f"Wallet has {wallet_balance / LAMPORTS_PER_SOL:.6f} SOL" - f" but trade requires ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL" - ) + if shortfall: + result["balance_warning"] = shortfall return result - if wallet_balance < required_lamports: + if shortfall: return { "error": "insufficient_balance", - "message": ( - f"Insufficient SOL balance. Have {wallet_balance / LAMPORTS_PER_SOL:.6f}" - f" SOL, need ~{required_lamports / LAMPORTS_PER_SOL:.6f} SOL." - ), + "message": f"Insufficient balance. {shortfall}.", "available_sol": wallet_balance / LAMPORTS_PER_SOL, "required_sol": required_lamports / LAMPORTS_PER_SOL, } - creator = _coerce_pubkey(state["creator"]) - is_mayhem = state.get("is_mayhem_mode", False) - - ixs = build_buy_exact_sol_in_instructions( - idl=idl, - mint=mint, - user=keypair.pubkey(), - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - creator=creator, - is_mayhem=is_mayhem, - spendable_sol_in=sol_lamports, - min_tokens_out=min_tokens_out, - token_program=token_program, + ixs = [create_idempotent_associated_token_account(user, user, mint, base_program)] + ixs.append( + build_buy_exact_quote_in_v2_instruction( + mint=mint, + creator=state["creator"], + user=user, + spendable_quote_in=amount_raw, + min_tokens_out=min_tokens_out, + quote_mint=quote_mint, + base_token_program=base_program, + quote_token_program=quote_program, + is_mayhem_mode=state["is_mayhem_mode"], + ) ) try: @@ -174,22 +236,22 @@ async def buy_token( ixs, [keypair], confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - compute_units=compute_units if compute_units is not None else 100_000, + priority_fee=eff_priority_fee, + compute_units=eff_compute_units, ) except TransactionFailedError as exc: return _handle_tx_error(exc, PUMP_SLIPPAGE_ERROR_CODES) - result = { - "action": "buy", - "mint": mint_str, - "sol_spent": sol_amount, - "tokens_received": tokens_out / (10**TOKEN_DECIMALS), - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result + return _tx_result( + "buy", + sig, + confirm, + venue="bonding_curve", + mint=mint_str, + quote_mint=str(quote_mint), + quote_symbol=symbol, + quote_spent=amount, + tokens_received=tokens_out / 10**TOKEN_DECIMALS, + ) finally: await client.close() @@ -206,58 +268,54 @@ async def sell_token( compute_units: int | None = None, dry_run: bool = False, ) -> dict: + """Sell tokens into the coin's bonding curve (sell_v2), paid in its quote asset.""" keypair = decrypt_keypair(Path(keystore_path), password) mint = parse_pubkey(mint_str, "mint address") if mint is None: return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - bonding_curve = derive_bonding_curve(mint) - token_program = await get_token_program_id(client, mint) - assoc_bc = derive_associated_bonding_curve(mint, bonding_curve, token_program) - - resp = await client.get_account_info(bonding_curve) - if not resp.value: + state = await _read_curve(client, mint) + if state is None: return {"error": "not_found", "message": f"No bonding curve for {mint_str}"} - state = idl.decode_account_data(resp.value.data, "BondingCurve", skip_discriminator=True) - - if state.get("complete"): + if state["complete"]: return {"error": "graduated", "message": "Token has graduated to PumpSwap."} - from spl.token.instructions import get_associated_token_address - - user_ata = get_associated_token_address(keypair.pubkey(), mint, token_program) + quote_mint = state["quote_mint"] + symbol = quote_symbol(quote_mint) + quote_program, quote_decimals = await get_mint_info(client, quote_mint) + base_program = await get_token_program_id(client, mint) + user = keypair.pubkey() + user_ata = find_ata(user, mint, base_program) if amount_str.lower() == "all": token_amount = await client.get_token_account_balance(user_ata) else: - token_amount = int(float(amount_str) * (10**TOKEN_DECIMALS)) - # Pre-trade token balance validation for specific amounts + token_amount = to_raw(amount_str, TOKEN_DECIMALS) actual_balance = await client.get_token_account_balance(user_ata) if actual_balance < token_amount: return { "error": "insufficient_balance", "message": ( f"Insufficient token balance. Have" - f" {actual_balance / (10**TOKEN_DECIMALS):.6f} tokens," - f" need {token_amount / (10**TOKEN_DECIMALS):.6f} tokens." + f" {actual_balance / 10**TOKEN_DECIMALS:.6f} tokens," + f" need {token_amount / 10**TOKEN_DECIMALS:.6f} tokens." ), - "available_tokens": actual_balance / (10**TOKEN_DECIMALS), - "required_tokens": token_amount / (10**TOKEN_DECIMALS), + "available_tokens": actual_balance / 10**TOKEN_DECIMALS, + "required_tokens": token_amount / 10**TOKEN_DECIMALS, } if token_amount <= 0: return {"error": "no_tokens", "message": "No tokens to sell."} - sol_out = calculate_sell_sol_out(state, token_amount) - min_sol = int(sol_out * (1 - slippage / 100)) + quote_out = calculate_sell_quote_out(state, token_amount) + min_quote_out = max(1, int(quote_out * (1 - slippage / 100))) if dry_run: - spot_price = get_token_price_sol(state) - tokens_human = token_amount / (10**TOKEN_DECIMALS) - sol_out_human = sol_out / LAMPORTS_PER_SOL - effective_price = sol_out_human / tokens_human if tokens_human > 0 else 0 + spot_price = get_token_price(state, quote_decimals) + tokens_human = token_amount / 10**TOKEN_DECIMALS + quote_out_human = quote_out / 10**quote_decimals + effective_price = quote_out_human / tokens_human if tokens_human > 0 else 0 price_impact = ( (effective_price - spot_price) / spot_price * 100 if spot_price > 0 else 0 ) @@ -266,31 +324,35 @@ async def sell_token( "action": "sell", "venue": "bonding_curve", "mint": mint_str, + "quote_mint": str(quote_mint), + "quote_symbol": symbol, "tokens_in": tokens_human, - "expected_sol": sol_out_human, - "effective_price_sol": effective_price, - "spot_price_sol": spot_price, + "expected_quote": quote_out_human, + "effective_price": effective_price, + "spot_price": spot_price, "price_impact_pct": round(price_impact, 4), - "min_sol_out": min_sol / LAMPORTS_PER_SOL, + "min_quote_out": min_quote_out / 10**quote_decimals, "slippage_pct": slippage, } - creator = _coerce_pubkey(state["creator"]) - is_mayhem = state.get("is_mayhem_mode", False) - is_cashback = state.get("is_cashback_coin", False) - - ixs = build_sell_instructions( - idl=idl, - mint=mint, - user=keypair.pubkey(), - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - creator=creator, - is_mayhem=is_mayhem, - token_amount=token_amount, - min_sol_output=min_sol, - is_cashback=is_cashback, - token_program=token_program, + ixs = [] + # A SOL coin pays out in native SOL; its quote account is only seed-checked. + if not is_sol(quote_mint): + ixs.append( + create_idempotent_associated_token_account(user, user, quote_mint, quote_program) + ) + ixs.append( + build_sell_v2_instruction( + mint=mint, + creator=state["creator"], + user=user, + token_amount=token_amount, + min_quote_out=min_quote_out, + quote_mint=quote_mint, + base_token_program=base_program, + quote_token_program=quote_program, + is_mayhem_mode=state["is_mayhem_mode"], + ) ) try: @@ -298,22 +360,28 @@ async def sell_token( ixs, [keypair], confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - compute_units=compute_units if compute_units is not None else 100_000, + priority_fee=priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE, + compute_units=compute_units + if compute_units is not None + else ( + PUMP_SELL_COMPUTE_UNITS + if is_sol(quote_mint) + else PUMP_NON_SOL_TRADE_COMPUTE_UNITS + ), ) except TransactionFailedError as exc: return _handle_tx_error(exc, PUMP_SLIPPAGE_ERROR_CODES) - result = { - "action": "sell", - "mint": mint_str, - "tokens_sold": token_amount / (10**TOKEN_DECIMALS), - "sol_received": sol_out / LAMPORTS_PER_SOL, - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result + return _tx_result( + "sell", + sig, + confirm, + venue="bonding_curve", + mint=mint_str, + quote_mint=str(quote_mint), + quote_symbol=symbol, + tokens_sold=token_amount / 10**TOKEN_DECIMALS, + quote_received=quote_out / 10**quote_decimals, + ) finally: await client.close() @@ -322,31 +390,50 @@ async def claim_cashback( rpc_url: str, keystore_path: str, password: str, + quote_mint_str: str | None = None, confirm: bool = False, priority_fee: int | None = None, compute_units: int | None = None, ) -> dict: - """Claim volume-based cashback rewards.""" + """Claim cashback accrued in one quote asset (SOL by default).""" keypair = decrypt_keypair(Path(keystore_path), password) + user = keypair.pubkey() client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - ix = build_claim_cashback_instruction(idl=idl, user=keypair.pubkey()) - sig = await client.send_tx( - [ix], - [keypair], - confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - compute_units=compute_units if compute_units is not None else 100_000, + quote_mint, quote_program = await _resolve_quote_arg(client, quote_mint_str) + accumulator = await client.get_account_info(find_user_volume_accumulator(user)) + if not accumulator.value: + return { + "error": "no_accumulator", + "message": "This wallet has no volume accumulator, so there is no cashback.", + } + ixs = [] + if not is_sol(quote_mint): + ixs.append( + create_idempotent_associated_token_account(user, user, quote_mint, quote_program) + ) + ixs.append( + build_claim_cashback_v2_instruction( + user=user, quote_mint=quote_mint, quote_token_program=quote_program + ) + ) + try: + sig = await client.send_tx( + ixs, + [keypair], + confirm=confirm, + priority_fee=priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE, + compute_units=compute_units if compute_units is not None else 100_000, + ) + except TransactionFailedError as exc: + return _handle_tx_error(exc, set()) + return _tx_result( + "claim_cashback", + sig, + confirm, + quote_mint=str(quote_mint), + quote_symbol=quote_symbol(quote_mint), ) - result = { - "action": "claim_cashback", - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result finally: await client.close() @@ -362,24 +449,19 @@ async def close_volume_accumulator( """Close volume accumulator account, recovering rent.""" keypair = decrypt_keypair(Path(keystore_path), password) client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - ix = build_close_volume_accumulator_instruction(idl=idl, user=keypair.pubkey()) - sig = await client.send_tx( - [ix], - [keypair], - confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - compute_units=compute_units if compute_units is not None else 100_000, - ) - result = { - "action": "close_volume_accumulator", - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result + ix = build_close_volume_accumulator_instruction(keypair.pubkey()) + try: + sig = await client.send_tx( + [ix], + [keypair], + confirm=confirm, + priority_fee=priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE, + compute_units=compute_units if compute_units is not None else 100_000, + ) + except TransactionFailedError as exc: + return _handle_tx_error(exc, set()) + return _tx_result("close_volume_accumulator", sig, confirm) finally: await client.close() @@ -393,56 +475,43 @@ async def migrate_token( priority_fee: int | None = None, compute_units: int | None = None, ) -> dict: - """Trigger migration from bonding curve to PumpSwap AMM.""" + """Move a complete bonding curve's liquidity into its PumpSwap pool (migrate_v2).""" keypair = decrypt_keypair(Path(keystore_path), password) mint = parse_pubkey(mint_str, "mint address") if mint is None: return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: - # Verify bonding curve is complete - bonding_curve = derive_bonding_curve(mint) - resp = await client.get_account_info(bonding_curve) - if not resp.value: + state = await _read_curve(client, mint) + if state is None: return {"error": "not_found", "message": f"No bonding curve for {mint_str}"} - state = idl.decode_account_data(resp.value.data, "BondingCurve", skip_discriminator=True) - if not state.get("complete"): + if not state["complete"]: return {"error": "not_complete", "message": "Bonding curve is not complete yet."} - # Read withdraw_authority from Global account - from pumpfun_cli.protocol.contracts import PUMP_GLOBAL - - global_resp = await client.get_account_info(PUMP_GLOBAL) - if not global_resp.value: - return {"error": "global_not_found", "message": "Global account not found."} - global_state = idl.decode_account_data( - global_resp.value.data, "Global", skip_discriminator=True - ) - withdraw_authority = _coerce_pubkey(global_state["withdraw_authority"]) + global_state = await fetch_global(client) + quote_mint = state["quote_mint"] + quote_program, _ = await get_mint_info(client, quote_mint) + base_program = await get_token_program_id(client, mint) - ix = build_migrate_instruction( - idl=idl, + ix = build_migrate_v2_instruction( mint=mint, user=keypair.pubkey(), - withdraw_authority=withdraw_authority, + withdraw_authority=global_state["withdraw_authority"], + quote_mint=quote_mint, + base_token_program=base_program, + quote_token_program=quote_program, ) - sig = await client.send_tx( - [ix], - [keypair], - compute_units=compute_units if compute_units is not None else 400_000, - confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - ) - result = { - "action": "migrate", - "mint": mint_str, - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result + try: + sig = await client.send_tx( + [ix], + [keypair], + compute_units=compute_units if compute_units is not None else 400_000, + confirm=confirm, + priority_fee=priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE, + ) + except TransactionFailedError as exc: + return _handle_tx_error(exc, set()) + return _tx_result("migrate", sig, confirm, mint=mint_str) finally: await client.close() @@ -451,63 +520,81 @@ async def collect_creator_fees( rpc_url: str, keystore_path: str, password: str, + quote_mint_str: str | None = None, confirm: bool = False, priority_fee: int | None = None, compute_units: int | None = None, ) -> dict: - """Collect creator fees from both pump.fun and PumpSwap vaults.""" - from spl.token.instructions import get_associated_token_address - - from pumpfun_cli.protocol.address import derive_amm_creator_vault, derive_creator_vault - from pumpfun_cli.protocol.contracts import TOKEN_PROGRAM, WSOL_MINT - from pumpfun_cli.protocol.instructions import ( - build_collect_coin_creator_fee_instruction, - build_collect_creator_fee_instruction, - ) + """Collect creator fees in one quote asset (SOL by default) from pump.fun and PumpSwap. + Vaults are per creator and per quote asset, so fees earned in USDC are + collected with a separate run. + """ keypair = decrypt_keypair(Path(keystore_path), password) creator = keypair.pubkey() client = RpcClient(rpc_url) - idl = IDLParser(str(IDL_PATH)) try: + quote_mint, quote_program = await _resolve_quote_arg(client, quote_mint_str) + _, quote_decimals = await get_mint_info(client, quote_mint) instructions = [] - pump_balance = 0 - pumpswap_balance = 0 - # Check pump.fun creator vault + # pump.fun: a SOL vault holds lamports itself, and keeps its rent-exempt + # minimum; any other quote sits in the vault's ATA. vault = derive_creator_vault(creator) - pump_balance = await client.get_balance(vault) + if is_sol(quote_mint): + rent_floor = await client.get_minimum_balance_for_rent_exemption(0) + pump_balance = max(0, await client.get_balance(vault) - rent_floor) + else: + pump_balance = await client.get_token_account_balance( + find_ata(vault, quote_mint, quote_program) + ) - if pump_balance > 0: - instructions.append(build_collect_creator_fee_instruction(idl=idl, creator=creator)) + amm_vault = derive_amm_creator_vault(creator) + pumpswap_balance = await client.get_token_account_balance( + find_ata(amm_vault, quote_mint, quote_program) + ) - # Check PumpSwap creator vault - amm_vault_authority = derive_amm_creator_vault(creator) - amm_vault_ata = get_associated_token_address(amm_vault_authority, WSOL_MINT, TOKEN_PROGRAM) - pumpswap_balance = await client.get_token_account_balance(amm_vault_ata) + if pump_balance <= 0 and pumpswap_balance <= 0: + return {"error": "no_fees", "message": "No creator fees to collect."} + if pumpswap_balance > 0 or not is_sol(quote_mint): + # Both payouts land in the creator's token account for the quote asset. + instructions.append( + create_idempotent_associated_token_account( + creator, creator, quote_mint, quote_program + ) + ) + if pump_balance > 0: + instructions.append( + build_collect_creator_fee_v2_instruction( + creator=creator, quote_mint=quote_mint, quote_token_program=quote_program + ) + ) if pumpswap_balance > 0: - instructions.append(build_collect_coin_creator_fee_instruction(creator=creator)) - - if not instructions: - return {"error": "no_fees", "message": "No creator fees to collect."} + instructions.append( + build_collect_coin_creator_fee_instruction( + creator=creator, quote_mint=quote_mint, quote_token_program=quote_program + ) + ) - sig = await client.send_tx( - instructions, - [keypair], - confirm=confirm, - priority_fee=priority_fee if priority_fee is not None else 200_000, - compute_units=compute_units if compute_units is not None else 100_000, + try: + sig = await client.send_tx( + instructions, + [keypair], + confirm=confirm, + priority_fee=priority_fee if priority_fee is not None else DEFAULT_PRIORITY_FEE, + compute_units=compute_units if compute_units is not None else 150_000, + ) + except TransactionFailedError as exc: + return _handle_tx_error(exc, set()) + return _tx_result( + "collect_creator_fee", + sig, + confirm, + quote_mint=str(quote_mint), + quote_symbol=quote_symbol(quote_mint), + pump_recovered=pump_balance / 10**quote_decimals, + pumpswap_recovered=pumpswap_balance / 10**quote_decimals, ) - result = { - "action": "collect_creator_fee", - "pump_recovered_sol": pump_balance / LAMPORTS_PER_SOL, - "pumpswap_recovered_sol": pumpswap_balance / LAMPORTS_PER_SOL, - "signature": sig, - "explorer": f"https://solscan.io/tx/{sig}", - } - if confirm: - result["confirmed"] = True - return result finally: await client.close() diff --git a/src/pumpfun_cli/core/wallet.py b/src/pumpfun_cli/core/wallet.py index a0fef7e..4511348 100644 --- a/src/pumpfun_cli/core/wallet.py +++ b/src/pumpfun_cli/core/wallet.py @@ -228,15 +228,15 @@ async def transfer_token( ) -> dict: """Transfer SPL tokens to another wallet, creating recipient ATA if needed.""" from spl.token.instructions import ( - TransferCheckedParams, create_idempotent_associated_token_account, get_associated_token_address, transfer_checked, ) + from spl.token.models import TransferCheckedParams + from pumpfun_cli.core.amounts import to_raw from pumpfun_cli.protocol.client import RpcClient - from pumpfun_cli.protocol.contracts import TOKEN_DECIMALS - from pumpfun_cli.protocol.pumpswap import get_token_program_id + from pumpfun_cli.protocol.quote import get_mint_info keypair = decrypt_keypair(Path(keystore_path), password) recipient = parse_pubkey(recipient_str, "recipient address") @@ -247,14 +247,14 @@ async def transfer_token( return invalid_pubkey_error(mint_str, "mint address") client = RpcClient(rpc_url) try: - token_program = await get_token_program_id(client, mint) + token_program, decimals = await get_mint_info(client, mint) sender_ata = get_associated_token_address(keypair.pubkey(), mint, token_program) recipient_ata = get_associated_token_address(recipient, mint, token_program) if amount_str.lower() == "all": raw_amount = await client.get_token_account_balance(sender_ata) else: - raw_amount = int(float(amount_str) * (10**TOKEN_DECIMALS)) + raw_amount = to_raw(amount_str, decimals) if raw_amount <= 0: return {"error": "no_tokens", "message": "No tokens to transfer."} @@ -274,7 +274,7 @@ async def transfer_token( dest=recipient_ata, owner=keypair.pubkey(), amount=raw_amount, - decimals=TOKEN_DECIMALS, + decimals=decimals, ) ), ] @@ -291,7 +291,7 @@ async def transfer_token( "from": str(keypair.pubkey()), "to": recipient_str, "mint": mint_str, - "amount": raw_amount / (10**TOKEN_DECIMALS), + "amount": raw_amount / (10**decimals), "signature": sig, "explorer": f"https://solscan.io/tx/{sig}", } diff --git a/src/pumpfun_cli/protocol/address.py b/src/pumpfun_cli/protocol/address.py index c342c16..5532cf2 100644 --- a/src/pumpfun_cli/protocol/address.py +++ b/src/pumpfun_cli/protocol/address.py @@ -1,33 +1,30 @@ -"""PDA derivation for pump.fun accounts.""" +"""PDA derivation for pump.fun and PumpSwap accounts.""" from solders.pubkey import Pubkey -from spl.token.instructions import get_associated_token_address from pumpfun_cli.protocol.contracts import ( ASSOCIATED_TOKEN_PROGRAM, MAYHEM_PROGRAM_ID, MAYHEM_SOL_VAULT, PUMP_AMM_PROGRAM, - PUMP_EVENT_AUTHORITY, - PUMP_FEE, PUMP_FEE_PROGRAM, - PUMP_GLOBAL, - PUMP_MAYHEM_FEE, PUMP_PROGRAM, - SYSTEM_PROGRAM, TOKEN_2022_PROGRAM, + WSOL_MINT, ) -def derive_bonding_curve(mint: Pubkey) -> Pubkey: - """Derive the bonding curve PDA for a token mint.""" - addr, _ = Pubkey.find_program_address([b"bonding-curve", bytes(mint)], PUMP_PROGRAM) +def find_ata(owner: Pubkey, mint: Pubkey, token_program: Pubkey) -> Pubkey: + """Associated token account; the address depends on which program owns the mint.""" + addr, _ = Pubkey.find_program_address( + [bytes(owner), bytes(token_program), bytes(mint)], ASSOCIATED_TOKEN_PROGRAM + ) return addr -def derive_bonding_curve_v2(mint: Pubkey) -> Pubkey: - """Derive the bonding curve v2 PDA for a token mint.""" - addr, _ = Pubkey.find_program_address([b"bonding-curve-v2", bytes(mint)], PUMP_PROGRAM) +def derive_bonding_curve(mint: Pubkey) -> Pubkey: + """Derive the bonding curve PDA for a token mint.""" + addr, _ = Pubkey.find_program_address([b"bonding-curve", bytes(mint)], PUMP_PROGRAM) return addr @@ -36,16 +33,12 @@ def derive_associated_bonding_curve( bonding_curve: Pubkey, token_program: Pubkey = TOKEN_2022_PROGRAM, ) -> Pubkey: - """Derive the associated bonding curve (ATA of bonding curve for the token).""" - addr, _ = Pubkey.find_program_address( - [bytes(bonding_curve), bytes(token_program), bytes(mint)], - ASSOCIATED_TOKEN_PROGRAM, - ) - return addr + """The bonding curve's token account for the coin.""" + return find_ata(bonding_curve, mint, token_program) def derive_creator_vault(creator: Pubkey) -> Pubkey: - """Derive the creator vault PDA.""" + """Derive the pump.fun creator vault PDA from BondingCurve.creator.""" addr, _ = Pubkey.find_program_address([b"creator-vault", bytes(creator)], PUMP_PROGRAM) return addr @@ -70,81 +63,31 @@ def derive_mayhem_state(mint: Pubkey) -> Pubkey: def derive_mayhem_token_vault(mint: Pubkey) -> Pubkey: """Derive the mayhem token vault (ATA of SOL_VAULT for the mint).""" - return get_associated_token_address(MAYHEM_SOL_VAULT, mint, TOKEN_2022_PROGRAM) + return find_ata(MAYHEM_SOL_VAULT, mint, TOKEN_2022_PROGRAM) def find_fee_config() -> Pubkey: - """Derive the fee config PDA.""" + """Derive the pump.fun fee config PDA (under the pump fees program).""" addr, _ = Pubkey.find_program_address([b"fee_config", bytes(PUMP_PROGRAM)], PUMP_FEE_PROGRAM) return addr -def get_fee_recipient(is_mayhem_mode: bool) -> Pubkey: - """Return the correct fee recipient based on mayhem mode.""" - return PUMP_MAYHEM_FEE if is_mayhem_mode else PUMP_FEE +def find_sharing_config(mint: Pubkey) -> Pubkey: + """Derive the creator-fee sharing config PDA (under the pump fees program).""" + addr, _ = Pubkey.find_program_address([b"sharing-config", bytes(mint)], PUMP_FEE_PROGRAM) + return addr -def get_buy_accounts( - mint: Pubkey, - bonding_curve: Pubkey, - assoc_bc: Pubkey, - user: Pubkey, - creator: Pubkey, - is_mayhem: bool, - token_program: Pubkey = TOKEN_2022_PROGRAM, -) -> dict[str, Pubkey]: - """Return ordered account dict for buy instruction (16 base + 1 remaining).""" - user_ata = get_associated_token_address(user, mint, token_program) - return { - "global": PUMP_GLOBAL, - "fee": get_fee_recipient(is_mayhem), - "mint": mint, - "bonding_curve": bonding_curve, - "associated_bonding_curve": assoc_bc, - "user_token_account": user_ata, - "user": user, - "system_program": SYSTEM_PROGRAM, - "token_program": token_program, - "creator_vault": derive_creator_vault(creator), - "event_authority": PUMP_EVENT_AUTHORITY, - "program": PUMP_PROGRAM, - "global_volume_accumulator": find_global_volume_accumulator(), - "user_volume_accumulator": find_user_volume_accumulator(user), - "fee_config": find_fee_config(), - "fee_program": PUMP_FEE_PROGRAM, - "bonding_curve_v2": derive_bonding_curve_v2(mint), - } - - -def get_sell_accounts( - mint: Pubkey, - bonding_curve: Pubkey, - assoc_bc: Pubkey, - user: Pubkey, - creator: Pubkey, - is_mayhem: bool, - token_program: Pubkey = TOKEN_2022_PROGRAM, -) -> dict[str, Pubkey]: - """Return ordered account dict for sell instruction (14 base + remaining).""" - user_ata = get_associated_token_address(user, mint, token_program) - return { - "global": PUMP_GLOBAL, - "fee": get_fee_recipient(is_mayhem), - "mint": mint, - "bonding_curve": bonding_curve, - "associated_bonding_curve": assoc_bc, - "user_token_account": user_ata, - "user": user, - "system_program": SYSTEM_PROGRAM, - "creator_vault": derive_creator_vault(creator), - "token_program": token_program, - "event_authority": PUMP_EVENT_AUTHORITY, - "program": PUMP_PROGRAM, - "fee_config": find_fee_config(), - "fee_program": PUMP_FEE_PROGRAM, - "user_volume_accumulator": find_user_volume_accumulator(user), - "bonding_curve_v2": derive_bonding_curve_v2(mint), - } +def find_holder_rewards(mint: Pubkey) -> Pubkey: + """The PDA a holder-reward coin carries as BondingCurve.creator instead of its creator.""" + addr, _ = Pubkey.find_program_address([b"holder-rewards", bytes(mint)], PUMP_PROGRAM) + return addr + + +def find_quote_control() -> Pubkey: + """Derive the QuoteControl PDA, the registry of admitted quote mints.""" + addr, _ = Pubkey.find_program_address([b"quote-control"], PUMP_PROGRAM) + return addr # --- PumpSwap AMM PDA derivation --- @@ -192,17 +135,21 @@ def derive_pool_authority(mint: Pubkey) -> Pubkey: return addr -def derive_amm_pool(mint: Pubkey) -> Pubkey: - """Derive the PumpSwap AMM pool PDA for a token mint. +def derive_amm_pool(mint: Pubkey, quote_mint: Pubkey = WSOL_MINT) -> Pubkey: + """Derive the canonical PumpSwap pool a pump.fun curve migrates into. - Uses the same seed layout as the migrate instruction: - seeds = ["pool", index_bytes, pool_authority, base_mint, quote_mint] + Seeds: ["pool", u16 0, pool_authority, base_mint, quote_mint]. The quote mint + is the curve's, so a coin priced in USDC graduates into a USDC pool. """ - from pumpfun_cli.protocol.contracts import WSOL_MINT - pool_authority = derive_pool_authority(mint) addr, _ = Pubkey.find_program_address( - [b"pool", bytes([0, 0]), bytes(pool_authority), bytes(mint), bytes(WSOL_MINT)], + [b"pool", bytes([0, 0]), bytes(pool_authority), bytes(mint), bytes(quote_mint)], PUMP_AMM_PROGRAM, ) return addr + + +def derive_amm_lp_mint(pool: Pubkey) -> Pubkey: + """Derive a PumpSwap pool's LP mint.""" + addr, _ = Pubkey.find_program_address([b"pool_lp_mint", bytes(pool)], PUMP_AMM_PROGRAM) + return addr diff --git a/src/pumpfun_cli/protocol/client.py b/src/pumpfun_cli/protocol/client.py index 41e4801..71d520f 100644 --- a/src/pumpfun_cli/protocol/client.py +++ b/src/pumpfun_cli/protocol/client.py @@ -1,18 +1,78 @@ """Stateless Solana RPC client for CLI usage.""" +import re + +import httpx from solana.rpc.async_api import AsyncClient -from solana.rpc.types import TokenAccountOpts, TxOpts +from solana.rpc.core import TokenAccountOpts, TxOptsModel from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price from solders.hash import Hash from solders.instruction import Instruction from solders.keypair import Keypair -from solders.message import MessageV0 +from solders.message import MessageV0, MessageV1, TransactionConfig from solders.pubkey import Pubkey +from solders.signature import Signature from solders.transaction import VersionedTransaction +from pumpfun_cli.protocol.contracts import TX_V1_LOADED_ACCOUNTS_DATA_LIMIT + DEFAULT_RPC_TIMEOUT = 30.0 -_ERR_PATTERN = r"InstructionError\(\((\d+),.*InstructionErrorCustom\((\d+)\)" +# Serialized size limits: a v0 transaction must fit one 1232-byte packet, a v1 +# transaction may carry up to 4096 bytes. +TX_V0_MAX_BYTES = 1232 +TX_V1_MAX_BYTES = 4096 + + +def transaction_size(msg: MessageV0 | MessageV1) -> int: + """Serialized size of a transaction carrying `msg` once signed.""" + signatures = msg.header.num_required_signatures + return len(bytes(msg)) + 1 + 64 * signatures + + +def build_message( + payer: Pubkey, + instructions: list[Instruction], + blockhash: Hash, + compute_units: int, + priority_fee: int, +) -> MessageV0 | MessageV1: + """Compile a v0 message, or a v1 message when the v0 one would not fit a packet. + + A PumpSwap buy on a cashback pool, or a launch that buys in the same + transaction, carries more accounts than v0 allows without lookup tables. + v1 raises the limit and states the compute budget in its header, so it + carries no ComputeBudget instructions; `priority_fee` (micro-lamports per + compute unit) becomes the total in lamports v1 expects. + + Raises: + ValueError: If the transaction is too large even for v1 + """ + budget_ixs = [set_compute_unit_limit(compute_units), set_compute_unit_price(priority_fee)] + msg_v0 = MessageV0.try_compile(payer, budget_ixs + instructions, [], blockhash) + if transaction_size(msg_v0) <= TX_V0_MAX_BYTES: + return msg_v0 + msg_v1 = MessageV1.try_compile( + payer, + instructions, + blockhash, + TransactionConfig( + compute_unit_limit=compute_units, + priority_fee=priority_fee * compute_units // 1_000_000, + loaded_accounts_data_size_limit=TX_V1_LOADED_ACCOUNTS_DATA_LIMIT, + ), + ) + size = transaction_size(msg_v1) + if size > TX_V1_MAX_BYTES: + raise ValueError(f"Transaction is {size} bytes, over the {TX_V1_MAX_BYTES}-byte limit") + return msg_v1 + + +# Matches both the solders repr and the JSON-RPC shape of a custom program error. +_ERR_PATTERNS = ( + r"InstructionError\(\((\d+),.*InstructionErrorCustom\((\d+)\)", + r"'InstructionError': \[(\d+), \{'Custom': (\d+)\}\]", +) class TransactionFailedError(RuntimeError): @@ -23,16 +83,15 @@ class TransactionFailedError(RuntimeError): raw_error: str def __init__(self, err_obj: object) -> None: - import re - self.raw_error = str(err_obj) - match = re.search(_ERR_PATTERN, self.raw_error) - if match: - self.instruction_index = int(match.group(1)) - self.error_code = int(match.group(2)) - else: - self.instruction_index = None - self.error_code = None + self.instruction_index = None + self.error_code = None + for pattern in _ERR_PATTERNS: + match = re.search(pattern, self.raw_error) + if match: + self.instruction_index = int(match.group(1)) + self.error_code = int(match.group(2)) + break super().__init__(self.raw_error) @@ -40,6 +99,8 @@ class RpcClient: """Simplified Solana RPC client — no background tasks, no lifecycle.""" def __init__(self, rpc_url: str, timeout: float = DEFAULT_RPC_TIMEOUT): + self.rpc_url = rpc_url + self.timeout = timeout self.rpc = AsyncClient(rpc_url, timeout=timeout) async def get_blockhash(self) -> Hash: @@ -49,6 +110,10 @@ async def get_blockhash(self) -> Hash: async def get_account_info(self, pubkey: Pubkey): return await self.rpc.get_account_info(pubkey) + async def get_multiple_accounts(self, pubkeys: list[Pubkey]): + resp = await self.rpc.get_multiple_accounts(pubkeys) + return resp.value + async def get_balance(self, pubkey: Pubkey, commitment: str | None = None) -> int: kwargs = {} if commitment is not None: @@ -56,6 +121,10 @@ async def get_balance(self, pubkey: Pubkey, commitment: str | None = None) -> in resp = await self.rpc.get_balance(pubkey, **kwargs) return resp.value + async def get_minimum_balance_for_rent_exemption(self, size: int) -> int: + resp = await self.rpc.get_minimum_balance_for_rent_exemption(size) + return resp.value + async def get_token_account_balance(self, token_account: Pubkey) -> int: try: resp = await self.rpc.get_token_account_balance(token_account) @@ -100,10 +169,27 @@ async def get_program_accounts( program_id: Pubkey, filters: list, ): - resp = await self.rpc.get_program_accounts(program_id, encoding="base64", filters=filters) return resp.value + async def _send_and_confirm(self, tx: VersionedTransaction, confirm: bool) -> str: + resp = await self.rpc.send_transaction(tx, opts=TxOptsModel(skip_preflight=True)) + signature = str(resp.value) + if confirm: + try: + await self.rpc.confirm_transaction( + resp.value, commitment="confirmed", sleep_seconds=1 + ) + status = await self.get_transaction(signature) + except Exception as exc: # noqa: BLE001 - the send already happened + raise RuntimeError( + f"Sent {signature} but could not confirm it ({type(exc).__name__}). " + f"Check it with: pumpfun tx-status {signature}" + ) from None + if status and status["err"]: + raise TransactionFailedError(status["err"]) + return signature + async def send_tx( self, instructions: list[Instruction], @@ -112,40 +198,58 @@ async def send_tx( priority_fee: int = 200_000, confirm: bool = False, ) -> str: - budget_ixs = [ - set_compute_unit_limit(compute_units), - set_compute_unit_price(priority_fee), - ] - all_ixs = budget_ixs + instructions + """Sign and send a transaction. `priority_fee` is in micro-lamports per compute unit. + + Sent as v0 when it fits, otherwise as v1; see `build_message`. + """ blockhash = await self.get_blockhash() - msg = MessageV0.try_compile(signers[0].pubkey(), all_ixs, [], blockhash) - tx = VersionedTransaction(msg, signers) - resp = await self.rpc.send_transaction(tx, opts=TxOpts(skip_preflight=True)) - if confirm: - await self.rpc.confirm_transaction(resp.value, commitment="confirmed", sleep_seconds=1) - tx_resp = await self.rpc.get_transaction( - resp.value, max_supported_transaction_version=0 - ) - if tx_resp.value and tx_resp.value.transaction.meta.err: - raise TransactionFailedError(tx_resp.value.transaction.meta.err) - return str(resp.value) + msg = build_message( + signers[0].pubkey(), instructions, blockhash, compute_units, priority_fee + ) + return await self._send_and_confirm(VersionedTransaction(msg, signers), confirm) async def get_transaction(self, signature_str: str) -> dict | None: - """Fetch a confirmed transaction by signature string. + """Fetch a confirmed transaction's outcome by signature. + + Reads the raw JSON-RPC result rather than a typed response, so a + transaction in any version is reported the same way. Returns a dict with slot, err, fee, block_time — or None if not found. """ - from solders.signature import Signature - - sig = Signature.from_string(signature_str) - resp = await self.rpc.get_transaction(sig, max_supported_transaction_version=0) - if resp.value is None: + Signature.from_string(signature_str) # reject a malformed signature before any I/O + payload = { + "jsonrpc": "2.0", + "id": 1, + "method": "getTransaction", + "params": [ + signature_str, + { + "encoding": "base64", + "commitment": "confirmed", + "maxSupportedTransactionVersion": 1, + }, + ], + } + # Errors are rebuilt without the request: RPC URLs usually carry an API key. + try: + async with httpx.AsyncClient(timeout=self.timeout) as http: + resp = await http.post(self.rpc_url, json=payload) + except httpx.HTTPError as exc: + raise RuntimeError(f"getTransaction failed: {type(exc).__name__}") from None + if resp.is_error: + raise RuntimeError(f"getTransaction failed: HTTP {resp.status_code}") + body = resp.json() + if body.get("error"): + raise RuntimeError(f"getTransaction failed: {body['error'].get('message')}") + result = body.get("result") + if result is None: return None + meta = result.get("meta") or {} return { - "slot": resp.value.slot, - "err": resp.value.transaction.meta.err, - "fee": resp.value.transaction.meta.fee, - "block_time": resp.value.block_time, + "slot": result.get("slot"), + "err": meta.get("err"), + "fee": meta.get("fee"), + "block_time": result.get("blockTime"), } async def close(self): diff --git a/src/pumpfun_cli/protocol/contracts.py b/src/pumpfun_cli/protocol/contracts.py index 55b8b72..7d85505 100644 --- a/src/pumpfun_cli/protocol/contracts.py +++ b/src/pumpfun_cli/protocol/contracts.py @@ -2,9 +2,13 @@ from solders.pubkey import Pubkey +# pump.fun's HTTP endpoints sit behind Cloudflare, which rejects the default +# python-httpx User-Agent; an identifying one is accepted. +HTTP_HEADERS = {"User-Agent": "pumpfun-cli (+https://github.com/chainstacklabs/pumpfun-cli)"} + # Solana constants LAMPORTS_PER_SOL = 1_000_000_000 -TOKEN_DECIMALS = 6 # pump.fun tokens are 6 decimals (not Solana's usual 9) +TOKEN_DECIMALS = 6 # every pump.fun coin has 6 decimals; other PumpSwap pools may not # System programs SYSTEM_PROGRAM = Pubkey.from_string("11111111111111111111111111111111") @@ -17,12 +21,11 @@ PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P") PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf") PUMP_EVENT_AUTHORITY = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1") -PUMP_FEE = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM") -PUMP_MAYHEM_FEE = Pubkey.from_string("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS") PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ") PUMP_MINT_AUTHORITY = Pubkey.from_string("TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM") -# Mayhem mode addresses +# Mayhem mode addresses. create_v2 needs all of them whether or not the coin is +# a mayhem coin; the is_mayhem_mode argument is what makes it one. MAYHEM_PROGRAM_ID = Pubkey.from_string("MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e") MAYHEM_GLOBAL_PARAMS = Pubkey.from_string("13ec7XdrjF3h3YcqBTFDSReRcUFwbCnJaAQspM4j6DDJ") MAYHEM_SOL_VAULT = Pubkey.from_string("BwWK17cbHxwWBKZkUYvzxLcNQ1YVyaFezduWbtm2de6s") @@ -31,40 +34,116 @@ PUMP_AMM_PROGRAM = Pubkey.from_string("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA") PUMP_SWAP_GLOBAL_CONFIG = Pubkey.from_string("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw") PUMP_SWAP_EVENT_AUTHORITY = Pubkey.from_string("GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR") -STANDARD_PUMPSWAP_FEE_RECIPIENT = Pubkey.from_string( - "7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ" -) + +# Quote assets. BondingCurve.quote_mint is all zeros for a SOL-paired coin, but +# every instruction takes wrapped SOL in that position. +DEFAULT_PUBKEY = Pubkey.default() WSOL_MINT = Pubkey.from_string("So11111111111111111111111111111111111111112") +USDC_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") +# Known without an RPC call. Any other quote mint is resolved from its account. +KNOWN_QUOTE_MINTS: dict[Pubkey, tuple[Pubkey, int]] = { + WSOL_MINT: (TOKEN_PROGRAM, 9), + USDC_MINT: (TOKEN_PROGRAM, 6), +} +# `decimals` sits at this offset in SPL Token and Token-2022 mints alike. +MINT_DECIMALS_OFFSET = 44 -# PumpSwap instruction discriminators -PUMPSWAP_BUY_DISCRIMINATOR = bytes.fromhex("66063d1201daebea") -PUMPSWAP_SELL_DISCRIMINATOR = bytes.fromhex("33e685a4017f83ad") +# Fee recipients for buy_v2 / sell_v2 / buy_exact_quote_in_v2: a normal one for +# ordinary coins, a reserved one for mayhem coins, and a buyback one on every +# trade. Picked at random per transaction to spread write contention. +NORMAL_FEE_RECIPIENTS = [ + Pubkey.from_string("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"), + Pubkey.from_string("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"), + Pubkey.from_string("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"), + Pubkey.from_string("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"), + Pubkey.from_string("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"), + Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"), + Pubkey.from_string("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"), + Pubkey.from_string("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"), +] +RESERVED_FEE_RECIPIENTS = [ + Pubkey.from_string("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"), + Pubkey.from_string("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"), + Pubkey.from_string("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"), + Pubkey.from_string("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"), + Pubkey.from_string("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"), + Pubkey.from_string("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"), + Pubkey.from_string("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"), + Pubkey.from_string("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"), +] +BUYBACK_FEE_RECIPIENTS = [ + Pubkey.from_string("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"), + Pubkey.from_string("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"), + Pubkey.from_string("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"), + Pubkey.from_string("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"), + Pubkey.from_string("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"), + Pubkey.from_string("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"), + Pubkey.from_string("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"), + Pubkey.from_string("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"), +] -# buy_exact_sol_in / buy_exact_quote_in discriminators -BUY_EXACT_SOL_IN_DISCRIMINATOR = bytes([56, 252, 116, 8, 158, 223, 205, 95]) -PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR = bytes([198, 46, 21, 82, 180, 217, 232, 112]) +# pump.fun instruction discriminators +BUY_V2_DISCRIMINATOR = bytes([184, 23, 238, 97, 103, 197, 211, 61]) +SELL_V2_DISCRIMINATOR = bytes([93, 246, 130, 60, 231, 233, 64, 178]) +BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR = bytes([194, 171, 28, 70, 104, 77, 91, 47]) +CREATE_V2_DISCRIMINATOR = bytes([214, 144, 76, 236, 95, 139, 49, 180]) +EXTEND_ACCOUNT_DISCRIMINATOR = bytes([234, 102, 194, 203, 150, 72, 62, 229]) +COLLECT_CREATOR_FEE_V2_DISCRIMINATOR = bytes([207, 17, 138, 242, 4, 34, 19, 56]) +CLAIM_CASHBACK_V2_DISCRIMINATOR = bytes([122, 243, 204, 65, 94, 116, 29, 55]) +CLOSE_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR = bytes([249, 69, 164, 218, 150, 103, 84, 138]) +MIGRATE_V2_DISCRIMINATOR = bytes([187, 203, 18, 31, 206, 237, 254, 41]) -# Creator fee collection discriminators -COLLECT_CREATOR_FEE_DISCRIMINATOR = bytes([20, 22, 86, 123, 198, 28, 219, 132]) +# PumpSwap instruction discriminators +PUMPSWAP_BUY_DISCRIMINATOR = bytes([102, 6, 61, 18, 1, 218, 235, 234]) +PUMPSWAP_SELL_DISCRIMINATOR = bytes([51, 230, 133, 164, 1, 127, 131, 173]) +PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR = bytes([198, 46, 21, 82, 180, 217, 232, 112]) COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR = bytes([160, 57, 89, 42, 181, 139, 43, 66]) +PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR = bytes([94, 6, 202, 115, 255, 96, 232, 183]) + +# Account discriminators +BONDING_CURVE_DISCRIMINATOR = bytes([23, 183, 248, 55, 96, 216, 172, 96]) +GLOBAL_DISCRIMINATOR = bytes([167, 232, 232, 177, 200, 108, 114, 127]) +QUOTE_CONTROL_DISCRIMINATOR = bytes([56, 244, 35, 238, 193, 213, 162, 201]) +POOL_DISCRIMINATOR = bytes([241, 154, 109, 4, 17, 177, 109, 188]) +GLOBAL_CONFIG_DISCRIMINATOR = bytes([149, 8, 156, 202, 160, 252, 176, 217]) -# PumpSwap pool data offsets +# PumpSwap Pool offsets, discriminator included. Live pools are 301 bytes. POOL_BASE_MINT_OFFSET = 43 +POOL_COIN_CREATOR_OFFSET = 211 POOL_MAYHEM_MODE_OFFSET = 243 -POOL_MAYHEM_MODE_MIN_SIZE = 244 -GLOBALCONFIG_RESERVED_FEE_OFFSET = 72 -GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET = 281 +POOL_CASHBACK_OFFSET = 244 +POOL_VIRTUAL_QUOTE_RESERVES_OFFSET = 245 # i128, added to the quote vault when pricing + +# PumpSwap GlobalConfig offsets, discriminator included. +GLOBALCONFIG_LP_FEE_BPS_OFFSET = 40 +GLOBALCONFIG_PROTOCOL_FEE_BPS_OFFSET = 48 +GLOBALCONFIG_PROTOCOL_FEE_RECIPIENTS_OFFSET = 57 # 8 pubkeys +GLOBALCONFIG_COIN_CREATOR_FEE_BPS_OFFSET = 313 +GLOBALCONFIG_RESERVED_FEE_RECIPIENT_OFFSET = 385 # the one mayhem pools pay +GLOBALCONFIG_BUYBACK_FEE_RECIPIENTS_OFFSET = 643 # 8 pubkeys # Solana rent / fee constants SOL_RENT_EXEMPT_MIN = 890_880 ATA_RENT_LAMPORTS = 2_039_280 +TOKEN_ACCOUNT_SIZE = 165 BASE_TX_FEE = 5_000 # On-chain slippage error codes PUMP_SLIPPAGE_ERROR_CODES: set[int] = {6002, 6003, 6042} PUMPSWAP_SLIPPAGE_ERROR_CODES: set[int] = {6004, 6040} -# PumpSwap compute budgets +# Compute budgets +PUMP_BUY_COMPUTE_UNITS = 180_000 +PUMP_SELL_COMPUTE_UNITS = 120_000 +# A non-SOL quote moves through Token-2022 transfers and may need its token +# account created, which costs several times what a SOL trade does. +PUMP_NON_SOL_TRADE_COMPUTE_UNITS = 400_000 PUMPSWAP_BUY_COMPUTE_UNITS = 400_000 PUMPSWAP_SELL_COMPUTE_UNITS = 300_000 PUMPSWAP_PRIORITY_FEE = 10_000 +LAUNCH_COMPUTE_UNITS = 400_000 + +# A v1 transaction states its budget in the message header. The loaded-accounts +# limit must be set explicitly: left unset it is zero rather than the network +# default, and the transaction is rejected. +TX_V1_LOADED_ACCOUNTS_DATA_LIMIT = 64 * 1024 * 1024 diff --git a/src/pumpfun_cli/protocol/curve.py b/src/pumpfun_cli/protocol/curve.py index c326279..abf8aa7 100644 --- a/src/pumpfun_cli/protocol/curve.py +++ b/src/pumpfun_cli/protocol/curve.py @@ -1,46 +1,118 @@ -"""Bonding curve math — ported from pumpfun-bonkfun-bot as flat module functions.""" +"""BondingCurve decoding and curve math. -from pumpfun_cli.protocol.contracts import LAMPORTS_PER_SOL, TOKEN_DECIMALS +A curve is 125 bytes as create_v2 allocates it, 151 for older coins, and +extend_account can grow it further. Every field read here sits at a fixed +offset regardless of length, so any length is decoded the same way. +""" -# Graduation threshold in SOL (curve completes at ~85 SOL real reserves). -_GRADUATION_SOL = 85.0 +import struct +from solders.pubkey import Pubkey -def calculate_buy_tokens_out(state: dict, sol_amount_lamports: int) -> int: - """Constant-product AMM buy: how many tokens for *sol_amount_lamports*.""" +from pumpfun_cli.protocol.contracts import BONDING_CURVE_DISCRIMINATOR, TOKEN_DECIMALS +from pumpfun_cli.protocol.quote import is_sol, normalize_quote_mint + +# Byte offsets, discriminator included. +_RESERVES_FORMAT = ( + " dict: + """Decode a BondingCurve account. + + `quote_mint` comes back normalized (wrapped SOL for a SOL-paired coin). + `creator_fee_bps` and `is_holder_reward` are None on a curve too short to + carry them, i.e. one created before those fields existed. + + Raises: + ValueError: If the discriminator is wrong or the data ends before a + field every trade needs + """ + if data[:8] != BONDING_CURVE_DISCRIMINATOR: + raise ValueError("Account is not a pump.fun bonding curve") + if len(data) <= _CASHBACK_OFFSET: + raise ValueError(f"Bonding curve data is only {len(data)} bytes") + ( + virtual_token, + virtual_quote, + real_token, + real_quote, + supply, + complete, + ) = struct.unpack_from(_RESERVES_FORMAT, data, 8) + + raw_quote = ( + Pubkey.from_bytes(data[_QUOTE_MINT_OFFSET : _QUOTE_MINT_OFFSET + 32]) + if len(data) >= _QUOTE_MINT_OFFSET + 32 + else None + ) + creator_fee_bps = ( + struct.unpack_from("= _CREATOR_FEE_BPS_OFFSET + 8 + else None + ) + is_holder_reward = ( + bool(data[_IS_HOLDER_REWARD_OFFSET]) if len(data) > _IS_HOLDER_REWARD_OFFSET else None + ) + return { + "virtual_token_reserves": virtual_token, + "virtual_quote_reserves": virtual_quote, + "real_token_reserves": real_token, + "real_quote_reserves": real_quote, + "token_total_supply": supply, + "complete": bool(complete), + "creator": Pubkey.from_bytes(data[_CREATOR_OFFSET : _CREATOR_OFFSET + 32]), + "is_mayhem_mode": bool(data[_MAYHEM_OFFSET]), + "is_cashback_coin": bool(data[_CASHBACK_OFFSET]), + "quote_mint": normalize_quote_mint(raw_quote), + "is_sol_paired": is_sol(raw_quote), + "creator_fee_bps": creator_fee_bps, + "is_holder_reward": is_holder_reward, + } + + +def calculate_buy_tokens_out(state: dict, quote_amount_raw: int) -> int: + """Constant-product buy: raw tokens out for `quote_amount_raw`, before fees.""" return ( - sol_amount_lamports + quote_amount_raw * state["virtual_token_reserves"] - // (state["virtual_sol_reserves"] + sol_amount_lamports) + // (state["virtual_quote_reserves"] + quote_amount_raw) ) -def calculate_sell_sol_out(state: dict, token_amount: int) -> int: - """Constant-product AMM sell: how many lamports for *token_amount* tokens.""" +def calculate_sell_quote_out(state: dict, token_amount: int) -> int: + """Constant-product sell: raw quote out for `token_amount` raw tokens, before fees.""" return ( token_amount - * state["virtual_sol_reserves"] + * state["virtual_quote_reserves"] // (state["virtual_token_reserves"] + token_amount) ) -def get_token_price_sol(state: dict) -> float: - """Current price in SOL per whole token (1e6 base units).""" +def get_token_price(state: dict, quote_decimals: int) -> float: + """Current price in whole quote units per whole token.""" if state["virtual_token_reserves"] == 0: return 0.0 - return ( - (state["virtual_sol_reserves"] / state["virtual_token_reserves"]) - * (10**TOKEN_DECIMALS) - / LAMPORTS_PER_SOL + return (state["virtual_quote_reserves"] / 10**quote_decimals) / ( + state["virtual_token_reserves"] / 10**TOKEN_DECIMALS ) -def get_bonding_progress(state: dict) -> float: - """Completion percentage as 0.0 – 1.0 (graduates at ~85 SOL).""" - return min( - state["real_sol_reserves"] / LAMPORTS_PER_SOL / _GRADUATION_SOL, - 1.0, - ) +def get_bonding_progress(state: dict, initial_real_token_reserves: int) -> float: + """Share of the curve sold, 0.0 – 1.0, measured against Global's opening token reserves.""" + if state["complete"]: + return 1.0 + if initial_real_token_reserves <= 0: + return 0.0 + sold = initial_real_token_reserves - state["real_token_reserves"] + return min(max(sold / initial_real_token_reserves, 0.0), 1.0) def is_graduated(state: dict) -> bool: diff --git a/src/pumpfun_cli/protocol/instructions.py b/src/pumpfun_cli/protocol/instructions.py index 597e720..668d6c9 100644 --- a/src/pumpfun_cli/protocol/instructions.py +++ b/src/pumpfun_cli/protocol/instructions.py @@ -1,890 +1,695 @@ -"""Build buy/sell/create instructions for the pump.fun program.""" +"""Instruction builders for the pump.fun and PumpSwap programs. +Bonding-curve trades use buy_v2 / buy_exact_quote_in_v2 / sell_v2: a fixed +account list, the same for every coin whatever its quote asset, mayhem or +cashback status. The IDL under-reports PumpSwap's swap accounts, so those +lists follow what the program reads on chain, documented at each builder. +""" + +import secrets import struct from solders.instruction import AccountMeta, Instruction from solders.pubkey import Pubkey +from solders.system_program import CreateAccountWithSeedParams, create_account_with_seed from spl.token.instructions import ( + close_account, create_idempotent_associated_token_account, - get_associated_token_address, + initialize_account, ) +from spl.token.models import CloseAccountParams, InitializeAccountParams from pumpfun_cli.protocol.address import ( derive_amm_creator_vault, derive_amm_fee_config, derive_amm_global_volume_accumulator, + derive_amm_lp_mint, + derive_amm_pool, derive_amm_pool_v2, derive_amm_user_volume_accumulator, - get_buy_accounts, - get_sell_accounts, + derive_bonding_curve, + derive_creator_vault, + derive_mayhem_state, + derive_pool_authority, + find_ata, + find_fee_config, + find_global_volume_accumulator, + find_quote_control, + find_sharing_config, + find_user_volume_accumulator, ) from pumpfun_cli.protocol.contracts import ( ASSOCIATED_TOKEN_PROGRAM, + ATA_RENT_LAMPORTS, + BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR, + BUY_V2_DISCRIMINATOR, + BUYBACK_FEE_RECIPIENTS, + CLAIM_CASHBACK_V2_DISCRIMINATOR, + CLOSE_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR, + COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR, + COLLECT_CREATOR_FEE_V2_DISCRIMINATOR, + CREATE_V2_DISCRIMINATOR, + EXTEND_ACCOUNT_DISCRIMINATOR, MAYHEM_GLOBAL_PARAMS, MAYHEM_PROGRAM_ID, MAYHEM_SOL_VAULT, + MIGRATE_V2_DISCRIMINATOR, + NORMAL_FEE_RECIPIENTS, PUMP_AMM_PROGRAM, PUMP_EVENT_AUTHORITY, PUMP_FEE_PROGRAM, PUMP_GLOBAL, + PUMP_MINT_AUTHORITY, PUMP_PROGRAM, PUMP_SWAP_EVENT_AUTHORITY, PUMP_SWAP_GLOBAL_CONFIG, - PUMPSWAP_BUY_DISCRIMINATOR, + PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR, + PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR, PUMPSWAP_SELL_DISCRIMINATOR, + RENT, + RESERVED_FEE_RECIPIENTS, + SELL_V2_DISCRIMINATOR, SYSTEM_PROGRAM, TOKEN_2022_PROGRAM, + TOKEN_ACCOUNT_SIZE, TOKEN_PROGRAM, WSOL_MINT, ) -from pumpfun_cli.protocol.idl_parser import IDLParser -# OptionBool(true) = 1 byte: the IDL defines OptionBool as struct { bool }, -# so it serialises to a single 0x01 byte, NOT 2 bytes (Option). +# OptionBool(true) is a single-field struct: one 0x01 byte, not a tagged Option. _TRACK_VOLUME = bytes([1]) -def build_buy_instructions( - idl: IDLParser, - mint: Pubkey, - user: Pubkey, - bonding_curve: Pubkey, - assoc_bc: Pubkey, - creator: Pubkey, - is_mayhem: bool, - token_amount: int, - max_sol_cost: int, - token_program: Pubkey = TOKEN_2022_PROGRAM, -) -> list[Instruction]: - """Build the ATA-creation + buy instructions for a pump.fun token. +def _meta(pubkey: Pubkey, writable: bool = False, signer: bool = False) -> AccountMeta: + return AccountMeta(pubkey=pubkey, is_signer=signer, is_writable=writable) - Returns a list of two instructions: - 1. Idempotent ATA creation for the user's token account. - 2. The buy instruction with 16 base accounts + bonding_curve_v2. - """ - # --- ATA creation instruction --- - ata_ix = create_idempotent_associated_token_account( - payer=user, owner=user, mint=mint, token_program_id=token_program - ) - # --- Buy instruction --- - accounts = get_buy_accounts( - mint=mint, - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - user=user, - creator=creator, - is_mayhem=is_mayhem, - token_program=token_program, - ) +# --- Quote settlement --- + - buy_accounts = [ - AccountMeta(pubkey=accounts["global"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["fee"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["bonding_curve"], is_signer=False, is_writable=True), - AccountMeta( - pubkey=accounts["associated_bonding_curve"], is_signer=False, is_writable=True +def quote_settlement_account( + user: Pubkey, quote_mint: Pubkey, quote_token_program: Pubkey, funding: int = 0 +) -> tuple[Pubkey, list[Instruction], list[Instruction]]: + """The account a PumpSwap trade pays from or into, with setup and teardown. + + Wrapped SOL goes through a throwaway account at a fresh seed, funded with + `funding` lamports and closed in the same transaction, so whatever is left + returns as SOL and any WSOL the wallet already holds is left alone. Any + other quote asset uses the wallet's ATA, created if missing. + + Returns: + (account, instructions before the trade, instructions after it) + """ + if quote_mint != WSOL_MINT: + ata = find_ata(user, quote_mint, quote_token_program) + create = create_idempotent_associated_token_account( + user, user, quote_mint, quote_token_program + ) + return ata, [create], [] + + seed = secrets.token_hex(16) + account = Pubkey.create_with_seed(user, seed, TOKEN_PROGRAM) + open_ixs = [ + create_account_with_seed( + CreateAccountWithSeedParams( + from_pubkey=user, + to_pubkey=account, + base=user, + seed=seed, + lamports=funding + ATA_RENT_LAMPORTS, + space=TOKEN_ACCOUNT_SIZE, + owner=TOKEN_PROGRAM, + ) ), - AccountMeta(pubkey=accounts["user_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["user"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts["system_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["creator_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["program"], is_signer=False, is_writable=False), - AccountMeta( - pubkey=accounts["global_volume_accumulator"], is_signer=False, is_writable=False + initialize_account( + InitializeAccountParams( + program_id=TOKEN_PROGRAM, account=account, mint=WSOL_MINT, owner=user + ) ), - AccountMeta(pubkey=accounts["user_volume_accumulator"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["fee_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["fee_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["bonding_curve_v2"], is_signer=False, is_writable=False), ] + close_ixs = [ + close_account( + CloseAccountParams(program_id=TOKEN_PROGRAM, account=account, dest=user, owner=user) + ) + ] + return account, open_ixs, close_ixs - discriminators = idl.get_instruction_discriminators() - instruction_data = ( - discriminators["buy"] + struct.pack(" Pubkey: + """A fee recipient from the set the program expects for this coin.""" + return secrets.choice(RESERVED_FEE_RECIPIENTS if is_mayhem_mode else NORMAL_FEE_RECIPIENTS) -def build_buy_exact_sol_in_instructions( - idl: IDLParser, +def build_v2_accounts( + *, mint: Pubkey, - user: Pubkey, - bonding_curve: Pubkey, - assoc_bc: Pubkey, creator: Pubkey, - is_mayhem: bool, - spendable_sol_in: int, - min_tokens_out: int, - token_program: Pubkey = TOKEN_2022_PROGRAM, -) -> list[Instruction]: - """Build ATA-creation + buy_exact_sol_in instructions. - - Same accounts as buy, different discriminator and arg semantics: - - spendable_sol_in: exact SOL budget (fees deducted from this) - - min_tokens_out: minimum tokens to receive (slippage protection) + user: Pubkey, + quote_mint: Pubkey, + base_token_program: Pubkey, + quote_token_program: Pubkey, + is_mayhem_mode: bool, + is_buy: bool, +) -> list[AccountMeta]: + """The account list buy_v2, buy_exact_quote_in_v2 and sell_v2 share. + + 27 accounts for a buy, 26 for a sell (no global_volume_accumulator). Every + account is mandatory. For a SOL-paired coin `quote_mint` is wrapped SOL; + the user's quote account is only seed-checked and must not be created. + + Args: + creator: BondingCurve.creator as read from chain. On a holder-reward or + fee-sharing coin it differs from whoever launched the coin, and the + creator vault derives from it. """ - from pumpfun_cli.protocol.contracts import BUY_EXACT_SOL_IN_DISCRIMINATOR - - ata_ix = create_idempotent_associated_token_account( - payer=user, owner=user, mint=mint, token_program_id=token_program - ) + bonding_curve = derive_bonding_curve(mint) + creator_vault = derive_creator_vault(creator) + user_volume_accumulator = find_user_volume_accumulator(user) + fee_recipient = pick_fee_recipient(is_mayhem_mode) + buyback_recipient = secrets.choice(BUYBACK_FEE_RECIPIENTS) - accounts = get_buy_accounts( - mint=mint, - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - user=user, - creator=creator, - is_mayhem=is_mayhem, - token_program=token_program, - ) + def quote_ata(owner: Pubkey) -> Pubkey: + return find_ata(owner, quote_mint, quote_token_program) - buy_accounts = [ - AccountMeta(pubkey=accounts["global"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["fee"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["bonding_curve"], is_signer=False, is_writable=True), - AccountMeta( - pubkey=accounts["associated_bonding_curve"], is_signer=False, is_writable=True - ), - AccountMeta(pubkey=accounts["user_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["user"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts["system_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["creator_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["program"], is_signer=False, is_writable=False), - AccountMeta( - pubkey=accounts["global_volume_accumulator"], is_signer=False, is_writable=False - ), - AccountMeta(pubkey=accounts["user_volume_accumulator"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["fee_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["fee_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["bonding_curve_v2"], is_signer=False, is_writable=False), + accounts = [ + _meta(PUMP_GLOBAL), + _meta(mint), + _meta(quote_mint), + _meta(base_token_program), + _meta(quote_token_program), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(fee_recipient, writable=True), + _meta(quote_ata(fee_recipient), writable=True), + _meta(buyback_recipient, writable=True), + _meta(quote_ata(buyback_recipient), writable=True), + _meta(bonding_curve, writable=True), + _meta(find_ata(bonding_curve, mint, base_token_program), writable=True), + _meta(quote_ata(bonding_curve), writable=True), + _meta(user, writable=True, signer=True), + _meta(find_ata(user, mint, base_token_program), writable=True), + _meta(quote_ata(user), writable=True), + _meta(creator_vault, writable=True), + _meta(quote_ata(creator_vault), writable=True), + _meta(find_sharing_config(mint)), + ] + if is_buy: + accounts.append(_meta(find_global_volume_accumulator())) + accounts += [ + _meta(user_volume_accumulator, writable=True), + _meta(quote_ata(user_volume_accumulator), writable=True), + _meta(find_fee_config()), + _meta(PUMP_FEE_PROGRAM), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), ] + return accounts - instruction_data = ( - BUY_EXACT_SOL_IN_DISCRIMINATOR - + struct.pack(" Instruction: + """Spend exactly `spendable_quote_in` (raw quote units, fees included), receive at least + `min_tokens_out` raw tokens.""" + return Instruction( + PUMP_PROGRAM, + BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR + + struct.pack(" list[Instruction]: - """Build the sell instruction for a pump.fun token. - - Returns a list containing a single sell instruction with 14 base accounts - plus remaining accounts (user_volume_accumulator for cashback, bonding_curve_v2 always). - """ - accounts = get_sell_accounts( - mint=mint, - bonding_curve=bonding_curve, - assoc_bc=assoc_bc, - user=user, - creator=creator, - is_mayhem=is_mayhem, - token_program=token_program, - ) - - sell_accounts = [ - AccountMeta(pubkey=accounts["global"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["fee"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts["bonding_curve"], is_signer=False, is_writable=True), - AccountMeta( - pubkey=accounts["associated_bonding_curve"], is_signer=False, is_writable=True + max_quote_cost: int, + quote_mint: Pubkey = WSOL_MINT, + base_token_program: Pubkey = TOKEN_2022_PROGRAM, + quote_token_program: Pubkey = TOKEN_PROGRAM, + is_mayhem_mode: bool = False, +) -> Instruction: + """Buy exactly `token_amount` raw tokens, spending at most `max_quote_cost` raw quote units.""" + return Instruction( + PUMP_PROGRAM, + BUY_V2_DISCRIMINATOR + struct.pack(" Instruction: + """Sell `token_amount` raw tokens for at least `min_quote_out` raw quote units.""" + return Instruction( + PUMP_PROGRAM, + SELL_V2_DISCRIMINATOR + struct.pack(" Pubkey: - """Derive the pump.fun mint authority PDA.""" - addr, _ = Pubkey.find_program_address([b"mint-authority"], PUMP_PROGRAM) - return addr +def encode_create_v2_trailing_args( + *, creator_fee_bps: int | None = None, is_holder_reward: bool | None = None +) -> bytes: + """Encode create_v2's trailing args: is_cashback_enabled, creator_fee_bps, is_holder_reward. -def _encode_borsh_string(s: str) -> bytes: - """Borsh string encoding: 4-byte LE length prefix + UTF-8 bytes.""" - encoded = s.encode("utf-8") - return struct.pack(" list[Instruction]: - """Build create_v2 token instruction for pump.fun (Token2022). - - The create_v2 instruction initialises a new token mint with Token-2022, - bonding curve, and optionally mayhem state. It requires two signers: - the wallet (user/payer) and the freshly generated mint keypair. + is_mayhem_mode: bool = False, + creator_fee_bps: int | None = None, + is_holder_reward: bool | None = None, + quote_mint: Pubkey = WSOL_MINT, + quote_token_program: Pubkey = TOKEN_PROGRAM, +) -> Instruction: + """Build create_v2, which mints the coin under Token-2022. - Account order follows the IDL ``create_v2`` instruction definition. + 20 accounts: the IDL's 16, then quote_mint, the curve's quote account, the + quote token program and QuoteControl as remaining accounts. They are sent + for SOL-paired coins too; leaving them out lands a SOL coin and silently + drops any creator fee. """ - from pumpfun_cli.protocol.address import ( - derive_associated_bonding_curve, - derive_bonding_curve, - derive_mayhem_state, - derive_mayhem_token_vault, - ) - bonding_curve = derive_bonding_curve(mint) - assoc_bc = derive_associated_bonding_curve(mint, bonding_curve, token_program) - mint_auth = _derive_mint_authority() - - mayhem_state = derive_mayhem_state(mint) - mayhem_token_vault = derive_mayhem_token_vault(mint) - - create_accounts = [ - AccountMeta(pubkey=mint, is_signer=True, is_writable=True), - AccountMeta(pubkey=mint_auth, is_signer=False, is_writable=False), - AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True), - AccountMeta(pubkey=assoc_bc, is_signer=False, is_writable=True), - AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False), - AccountMeta(pubkey=user, is_signer=True, is_writable=True), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_2022_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM, is_signer=False, is_writable=False), - # Mayhem accounts are always required by create_v2 per IDL, - # regardless of is_mayhem_mode flag value. - AccountMeta(pubkey=MAYHEM_PROGRAM_ID, is_signer=False, is_writable=True), - AccountMeta(pubkey=MAYHEM_GLOBAL_PARAMS, is_signer=False, is_writable=False), - AccountMeta(pubkey=MAYHEM_SOL_VAULT, is_signer=False, is_writable=True), - AccountMeta(pubkey=mayhem_state, is_signer=False, is_writable=True), - AccountMeta(pubkey=mayhem_token_vault, is_signer=False, is_writable=True), + accounts = [ + _meta(mint, writable=True, signer=True), + _meta(PUMP_MINT_AUTHORITY), + _meta(bonding_curve, writable=True), + _meta(find_ata(bonding_curve, mint, TOKEN_2022_PROGRAM), writable=True), + _meta(PUMP_GLOBAL), + _meta(user, writable=True, signer=True), + _meta(SYSTEM_PROGRAM), + _meta(TOKEN_2022_PROGRAM), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(MAYHEM_PROGRAM_ID, writable=True), + _meta(MAYHEM_GLOBAL_PARAMS), + _meta(MAYHEM_SOL_VAULT, writable=True), + _meta(derive_mayhem_state(mint), writable=True), + _meta(find_ata(MAYHEM_SOL_VAULT, mint, TOKEN_2022_PROGRAM), writable=True), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + _meta(quote_mint), + _meta(find_ata(bonding_curve, quote_mint, quote_token_program), writable=True), + _meta(quote_token_program), + _meta(find_quote_control()), ] - create_accounts.extend( - [ - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - ) - - discriminators = idl.get_instruction_discriminators() - - instruction_data = ( - discriminators["create_v2"] - + _encode_borsh_string(name) - + _encode_borsh_string(symbol) - + _encode_borsh_string(uri) - + bytes(user) # creator arg - + struct.pack(" bytes: + encoded = value.encode("utf-8") + return struct.pack(" Instruction: - """Build extend_account instruction to expand bonding curve account size. - - Required after create_v2 for frontend visibility. - """ - accounts = [ - AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True), - AccountMeta(pubkey=user, is_signer=False, is_writable=False), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - - discriminators = idl.get_instruction_discriminators() +def build_extend_account_instruction(bonding_curve: Pubkey, user: Pubkey) -> Instruction: + """Grow the curve past the 125 bytes create_v2 allocates, as pump.fun's frontend expects.""" return Instruction( - program_id=PUMP_PROGRAM, - accounts=accounts, - data=discriminators["extend_account"], + PUMP_PROGRAM, + EXTEND_ACCOUNT_DISCRIMINATOR, + [ + _meta(bonding_curve, writable=True), + _meta(user, writable=True, signer=True), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + ], ) -# --- PumpSwap AMM instruction builders --- +# --- PumpSwap AMM --- -def build_pumpswap_buy_instructions( +def _pumpswap_accounts( + *, user: Pubkey, pool_address: Pubkey, pool: dict, - token_program_id: Pubkey, + tokens: dict, + user_quote_account: Pubkey, fee_recipient: Pubkey, - fee_recipient_ata: Pubkey, - amount_out: int, - max_sol_in: int, - sol_wrap_lamports: int, -) -> list[Instruction]: - """Build PumpSwap buy instructions (WSOL wrap + buy swap). - - Returns 5 instructions: - 1. Create WSOL ATA (idempotent) - 2. Transfer SOL to WSOL ATA - 3. Sync native - 4. Create base token ATA (idempotent) - 5. Buy instruction (23 IDL accounts + 2 trailing: vol_wsol_ata, pool_v2) + buyback_recipient: Pubkey, + is_buy: bool, +) -> list[AccountMeta]: + """PumpSwap's swap accounts: the IDL's list, then the extras the program reads. + + After the IDL's 23 (buy) or 21 (sell): the cashback account(s) on a + cashback pool, `pool-v2` on a canonical pool only, and the buyback + recipient with its quote account last. The program reads that pair from + the end, so sending `pool-v2` on a non-canonical pool shifts it and the + trade fails with 6053 (BuybackFeeRecipientNotAuthorized). """ - from solders.system_program import TransferParams, transfer - from spl.token.instructions import SyncNativeParams, sync_native - - user_wsol_ata = get_associated_token_address(user, WSOL_MINT, TOKEN_PROGRAM) - user_token_ata = get_associated_token_address(user, pool["base_mint"], token_program_id) - - coin_creator = pool["coin_creator"] - creator_vault_authority = derive_amm_creator_vault(coin_creator) - creator_vault_ata = get_associated_token_address( - creator_vault_authority, WSOL_MINT, TOKEN_PROGRAM - ) + base_mint = pool["base_mint"] + quote_mint = pool["quote_mint"] + base_program = tokens["base_token_program"] + quote_program = tokens["quote_token_program"] + creator_vault = derive_amm_creator_vault(pool["coin_creator"]) + user_volume_accumulator = derive_amm_user_volume_accumulator(user) - # 1. Create WSOL ATA - create_wsol_ata = create_idempotent_associated_token_account( - payer=user, - owner=user, - mint=WSOL_MINT, - token_program_id=TOKEN_PROGRAM, - ) + def quote_ata(owner: Pubkey) -> Pubkey: + return find_ata(owner, quote_mint, quote_program) - # 2. Transfer SOL to WSOL ATA - transfer_ix = transfer( - TransferParams( - from_pubkey=user, - to_pubkey=user_wsol_ata, - lamports=sol_wrap_lamports, - ) - ) - - # 3. Sync native - sync_ix = sync_native( - SyncNativeParams( - program_id=TOKEN_PROGRAM, - account=user_wsol_ata, - ) - ) - - # 4. Create base token ATA - create_token_ata = create_idempotent_associated_token_account( - payer=user, - owner=user, - mint=pool["base_mint"], - token_program_id=token_program_id, - ) - - # 5. Buy instruction (24 accounts: 17 IDL + 6 remaining + pool_v2 trailing) - pool_v2 = derive_amm_pool_v2(pool["base_mint"]) - - buy_accounts = [ - AccountMeta(pubkey=pool_address, is_signer=False, is_writable=True), - AccountMeta(pubkey=user, is_signer=True, is_writable=True), - AccountMeta(pubkey=PUMP_SWAP_GLOBAL_CONFIG, is_signer=False, is_writable=False), - AccountMeta(pubkey=pool["base_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=WSOL_MINT, is_signer=False, is_writable=False), - AccountMeta(pubkey=user_token_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=user_wsol_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool["pool_base_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=pool["pool_quote_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=fee_recipient, is_signer=False, is_writable=False), - AccountMeta(pubkey=fee_recipient_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=token_program_id, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_SWAP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_AMM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=creator_vault_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=creator_vault_authority, is_signer=False, is_writable=False), - AccountMeta( - pubkey=derive_amm_global_volume_accumulator(), is_signer=False, is_writable=False - ), - AccountMeta( - pubkey=derive_amm_user_volume_accumulator(user), is_signer=False, is_writable=True - ), - AccountMeta(pubkey=derive_amm_fee_config(), is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=pool_v2, is_signer=False, is_writable=False), + accounts = [ + _meta(pool_address, writable=True), + _meta(user, writable=True, signer=True), + _meta(PUMP_SWAP_GLOBAL_CONFIG), + _meta(base_mint), + _meta(quote_mint), + _meta(find_ata(user, base_mint, base_program), writable=True), + _meta(user_quote_account, writable=True), + _meta(pool["pool_base_token_account"], writable=True), + _meta(pool["pool_quote_token_account"], writable=True), + _meta(fee_recipient), + _meta(quote_ata(fee_recipient), writable=True), + _meta(base_program), + _meta(quote_program), + _meta(SYSTEM_PROGRAM), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(PUMP_SWAP_EVENT_AUTHORITY), + _meta(PUMP_AMM_PROGRAM), + _meta(quote_ata(creator_vault), writable=True), + _meta(creator_vault), ] - - instruction_data = ( - PUMPSWAP_BUY_DISCRIMINATOR - + struct.pack(" list[Instruction]: - """Build PumpSwap buy_exact_quote_in instructions (WSOL wrap + buy swap). + """Spend exactly `spendable_quote_in` raw quote units on a PumpSwap pool. - Same 5 instructions and 23 accounts as regular buy, but uses - buy_exact_quote_in discriminator with (spendable_quote_in, min_base_amount_out) args. + A SOL pool is paid from a throwaway WSOL account funded with `wsol_funding` + lamports and closed afterwards, so unspent SOL comes straight back. """ - from solders.system_program import TransferParams, transfer - from spl.token.instructions import SyncNativeParams, sync_native - - from pumpfun_cli.protocol.contracts import PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR - - user_wsol_ata = get_associated_token_address(user, WSOL_MINT, TOKEN_PROGRAM) - user_token_ata = get_associated_token_address(user, pool["base_mint"], token_program_id) - - coin_creator = pool["coin_creator"] - creator_vault_authority = derive_amm_creator_vault(coin_creator) - creator_vault_ata = get_associated_token_address( - creator_vault_authority, WSOL_MINT, TOKEN_PROGRAM + user_quote, open_ixs, close_ixs = quote_settlement_account( + user, pool["quote_mint"], tokens["quote_token_program"], funding=wsol_funding ) - - create_wsol_ata = create_idempotent_associated_token_account( - payer=user, - owner=user, - mint=WSOL_MINT, - token_program_id=TOKEN_PROGRAM, + create_base_ata = create_idempotent_associated_token_account( + user, user, pool["base_mint"], tokens["base_token_program"] ) - transfer_ix = transfer( - TransferParams( - from_pubkey=user, - to_pubkey=user_wsol_ata, - lamports=sol_wrap_lamports, - ) - ) - sync_ix = sync_native( - SyncNativeParams( - program_id=TOKEN_PROGRAM, - account=user_wsol_ata, - ) - ) - create_token_ata = create_idempotent_associated_token_account( - payer=user, - owner=user, - mint=pool["base_mint"], - token_program_id=token_program_id, - ) - - pool_v2 = derive_amm_pool_v2(pool["base_mint"]) - - buy_accounts = [ - AccountMeta(pubkey=pool_address, is_signer=False, is_writable=True), - AccountMeta(pubkey=user, is_signer=True, is_writable=True), - AccountMeta(pubkey=PUMP_SWAP_GLOBAL_CONFIG, is_signer=False, is_writable=False), - AccountMeta(pubkey=pool["base_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=WSOL_MINT, is_signer=False, is_writable=False), - AccountMeta(pubkey=user_token_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=user_wsol_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool["pool_base_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=pool["pool_quote_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=fee_recipient, is_signer=False, is_writable=False), - AccountMeta(pubkey=fee_recipient_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=token_program_id, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_SWAP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_AMM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=creator_vault_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=creator_vault_authority, is_signer=False, is_writable=False), - AccountMeta( - pubkey=derive_amm_global_volume_accumulator(), is_signer=False, is_writable=False - ), - AccountMeta( - pubkey=derive_amm_user_volume_accumulator(user), is_signer=False, is_writable=True - ), - AccountMeta(pubkey=derive_amm_fee_config(), is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=pool_v2, is_signer=False, is_writable=False), - ] - - instruction_data = ( + accounts = _pumpswap_accounts( + user=user, + pool_address=pool_address, + pool=pool, + tokens=tokens, + user_quote_account=user_quote, + fee_recipient=fee_recipient, + buyback_recipient=buyback_recipient, + is_buy=True, + ) + data = ( PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR - + struct.pack(" Instruction: - """Build init_user_volume_accumulator instruction for PumpSwap AMM.""" - INIT_DISCRIMINATOR = bytes([94, 6, 202, 115, 255, 96, 232, 183]) - user_vol = derive_amm_user_volume_accumulator(user) - accounts = [ - AccountMeta(pubkey=user, is_signer=True, is_writable=True), - AccountMeta(pubkey=user, is_signer=False, is_writable=False), - AccountMeta(pubkey=user_vol, is_signer=False, is_writable=True), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_SWAP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_AMM_PROGRAM, is_signer=False, is_writable=False), - ] - return Instruction(program_id=PUMP_AMM_PROGRAM, accounts=accounts, data=INIT_DISCRIMINATOR) + swap = Instruction(PUMP_AMM_PROGRAM, data, accounts) + return [*open_ixs, create_base_ata, swap, *close_ixs] def build_pumpswap_sell_instructions( + *, user: Pubkey, pool_address: Pubkey, pool: dict, - token_program_id: Pubkey, + tokens: dict, fee_recipient: Pubkey, - fee_recipient_ata: Pubkey, + buyback_recipient: Pubkey, token_amount: int, - min_sol_out: int, + min_quote_out: int, ) -> list[Instruction]: - """Build PumpSwap sell instructions (WSOL ATA + sell swap). - - Returns 2 instructions: - 1. Create WSOL ATA (idempotent) - needed to receive SOL - 2. Sell instruction (21 IDL accounts + 1 trailing: pool_v2) - """ - user_wsol_ata = get_associated_token_address(user, WSOL_MINT, TOKEN_PROGRAM) - user_token_ata = get_associated_token_address(user, pool["base_mint"], token_program_id) - - coin_creator = pool["coin_creator"] - creator_vault_authority = derive_amm_creator_vault(coin_creator) - creator_vault_ata = get_associated_token_address( - creator_vault_authority, WSOL_MINT, TOKEN_PROGRAM + """Sell `token_amount` raw base tokens on a PumpSwap pool for at least `min_quote_out`.""" + user_quote, open_ixs, close_ixs = quote_settlement_account( + user, pool["quote_mint"], tokens["quote_token_program"] ) - - # 1. Create WSOL ATA - create_wsol_ata = create_idempotent_associated_token_account( - payer=user, - owner=user, - mint=WSOL_MINT, - token_program_id=TOKEN_PROGRAM, + accounts = _pumpswap_accounts( + user=user, + pool_address=pool_address, + pool=pool, + tokens=tokens, + user_quote_account=user_quote, + fee_recipient=fee_recipient, + buyback_recipient=buyback_recipient, + is_buy=False, ) + data = PUMPSWAP_SELL_DISCRIMINATOR + struct.pack(" Instruction: + """Build init_user_volume_accumulator instruction for PumpSwap AMM.""" + return Instruction( + PUMP_AMM_PROGRAM, + PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR, + [ + _meta(user, writable=True, signer=True), + _meta(user), + _meta(derive_amm_user_volume_accumulator(user), writable=True), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_SWAP_EVENT_AUTHORITY), + _meta(PUMP_AMM_PROGRAM), + ], ) - return [create_wsol_ata, sell_ix] +# --- Payouts, cleanup and migration --- -# --- Bonding curve extras & migration instruction builders --- - -def build_claim_cashback_instruction( - idl: IDLParser, - user: Pubkey, +def build_claim_cashback_v2_instruction( + *, user: Pubkey, quote_mint: Pubkey = WSOL_MINT, quote_token_program: Pubkey = TOKEN_PROGRAM ) -> Instruction: - """Build claim_cashback instruction — claims volume-based cashback rewards.""" - from pumpfun_cli.protocol.address import find_user_volume_accumulator - - accounts = [ - AccountMeta(pubkey=user, is_signer=False, is_writable=True), - AccountMeta(pubkey=find_user_volume_accumulator(user), is_signer=False, is_writable=True), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - - discriminators = idl.get_instruction_discriminators() + """Pay out the cashback accrued on the user's volume accumulator in one quote asset.""" + accumulator = find_user_volume_accumulator(user) return Instruction( - program_id=PUMP_PROGRAM, - accounts=accounts, - data=discriminators["claim_cashback"], + PUMP_PROGRAM, + CLAIM_CASHBACK_V2_DISCRIMINATOR, + [ + _meta(user, writable=True), + _meta(accumulator, writable=True), + _meta(quote_mint), + _meta(quote_token_program), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(find_ata(accumulator, quote_mint, quote_token_program), writable=True), + _meta(find_ata(user, quote_mint, quote_token_program), writable=True), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + ], ) -def build_close_volume_accumulator_instruction( - idl: IDLParser, - user: Pubkey, -) -> Instruction: +def build_close_volume_accumulator_instruction(user: Pubkey) -> Instruction: """Build close_user_volume_accumulator instruction — recovers rent.""" - from pumpfun_cli.protocol.address import find_user_volume_accumulator - - accounts = [ - AccountMeta(pubkey=user, is_signer=True, is_writable=True), - AccountMeta(pubkey=find_user_volume_accumulator(user), is_signer=False, is_writable=True), - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - - discriminators = idl.get_instruction_discriminators() - return Instruction( - program_id=PUMP_PROGRAM, - accounts=accounts, - data=discriminators["close_user_volume_accumulator"], - ) - - -def build_migrate_instruction( - idl: IDLParser, - mint: Pubkey, - user: Pubkey, - withdraw_authority: Pubkey, -) -> Instruction: - """Build migrate instruction — triggers graduation from bonding curve to PumpSwap AMM. - - Requires 24 accounts. The withdraw_authority must be read from the Global account on-chain. - """ - from pumpfun_cli.protocol.address import ( - derive_associated_bonding_curve, - derive_bonding_curve, - derive_pool_authority, - ) - - bonding_curve = derive_bonding_curve(mint) - assoc_bc = derive_associated_bonding_curve(mint, bonding_curve, TOKEN_2022_PROGRAM) - pool_authority = derive_pool_authority(mint) - - # Derive pool PDA on AMM program: seeds = [b"pool", [0,0], pool_authority, mint, wsol_mint] - pool, _ = Pubkey.find_program_address( - [b"pool", bytes([0, 0]), bytes(pool_authority), bytes(mint), bytes(WSOL_MINT)], - PUMP_AMM_PROGRAM, - ) - - # LP mint PDA on AMM program: seeds = [b"pool_lp_mint", pool] - lp_mint, _ = Pubkey.find_program_address([b"pool_lp_mint", bytes(pool)], PUMP_AMM_PROGRAM) - - # ATAs - pool_authority_mint_account = get_associated_token_address( - pool_authority, - mint, - TOKEN_2022_PROGRAM, - ) - pool_authority_wsol_account = get_associated_token_address( - pool_authority, - WSOL_MINT, - TOKEN_PROGRAM, - ) - user_pool_token_account = get_associated_token_address( - pool_authority, - lp_mint, - TOKEN_2022_PROGRAM, - ) - pool_base_token_account = get_associated_token_address( - pool, - mint, - TOKEN_2022_PROGRAM, - ) - pool_quote_token_account = get_associated_token_address( - pool, - WSOL_MINT, - TOKEN_PROGRAM, - ) - - accounts = [ - AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False), - AccountMeta(pubkey=withdraw_authority, is_signer=False, is_writable=True), - AccountMeta(pubkey=mint, is_signer=False, is_writable=False), - AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True), - AccountMeta(pubkey=assoc_bc, is_signer=False, is_writable=True), - AccountMeta(pubkey=user, is_signer=True, is_writable=False), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_AMM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=pool, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool_authority, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool_authority_mint_account, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool_authority_wsol_account, is_signer=False, is_writable=True), - AccountMeta(pubkey=PUMP_SWAP_GLOBAL_CONFIG, is_signer=False, is_writable=False), - AccountMeta(pubkey=WSOL_MINT, is_signer=False, is_writable=False), - AccountMeta(pubkey=lp_mint, is_signer=False, is_writable=True), - AccountMeta(pubkey=user_pool_token_account, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool_base_token_account, is_signer=False, is_writable=True), - AccountMeta(pubkey=pool_quote_token_account, is_signer=False, is_writable=True), - AccountMeta(pubkey=TOKEN_2022_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_SWAP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - - discriminators = idl.get_instruction_discriminators() return Instruction( - program_id=PUMP_PROGRAM, - accounts=accounts, - data=discriminators["migrate"], + PUMP_PROGRAM, + CLOSE_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR, + [ + _meta(user, writable=True, signer=True), + _meta(find_user_volume_accumulator(user), writable=True), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + ], ) -def build_collect_creator_fee_instruction( - idl: "IDLParser", +def build_collect_creator_fee_v2_instruction( + *, creator: Pubkey, + quote_mint: Pubkey = WSOL_MINT, + quote_token_program: Pubkey = TOKEN_PROGRAM, ) -> Instruction: - """Build collect_creator_fee instruction — collects fees from pump.fun creator vault.""" - from pumpfun_cli.protocol.address import derive_creator_vault - from pumpfun_cli.protocol.contracts import COLLECT_CREATOR_FEE_DISCRIMINATOR - - accounts = [ - AccountMeta(pubkey=creator, is_signer=False, is_writable=True), - AccountMeta(pubkey=derive_creator_vault(creator), is_signer=False, is_writable=True), - AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), - ] - + """Sweep a creator's pump.fun vault for one quote asset into the creator's wallet.""" + vault = derive_creator_vault(creator) return Instruction( - program_id=PUMP_PROGRAM, - accounts=accounts, - data=COLLECT_CREATOR_FEE_DISCRIMINATOR, + PUMP_PROGRAM, + COLLECT_CREATOR_FEE_V2_DISCRIMINATOR, + [ + _meta(creator, writable=True), + _meta(find_ata(creator, quote_mint, quote_token_program), writable=True), + _meta(vault, writable=True), + _meta(find_ata(vault, quote_mint, quote_token_program), writable=True), + _meta(quote_mint), + _meta(quote_token_program), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + ], ) def build_collect_coin_creator_fee_instruction( + *, creator: Pubkey, + quote_mint: Pubkey = WSOL_MINT, + quote_token_program: Pubkey = TOKEN_PROGRAM, ) -> Instruction: - """Build collect_coin_creator_fee instruction — collects fees from PumpSwap creator vault.""" - from pumpfun_cli.protocol.address import derive_amm_creator_vault - from pumpfun_cli.protocol.contracts import ( + """Sweep a creator's PumpSwap vault for one quote asset into the creator's token account.""" + vault_authority = derive_amm_creator_vault(creator) + return Instruction( + PUMP_AMM_PROGRAM, COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR, - WSOL_MINT, + [ + _meta(quote_mint), + _meta(quote_token_program), + _meta(creator), + _meta(vault_authority), + _meta(find_ata(vault_authority, quote_mint, quote_token_program), writable=True), + _meta(find_ata(creator, quote_mint, quote_token_program), writable=True), + _meta(PUMP_SWAP_EVENT_AUTHORITY), + _meta(PUMP_AMM_PROGRAM), + ], ) - creator_vault_authority = derive_amm_creator_vault(creator) - creator_vault_ata = get_associated_token_address( - creator_vault_authority, WSOL_MINT, TOKEN_PROGRAM - ) - creator_wsol_ata = get_associated_token_address(creator, WSOL_MINT, TOKEN_PROGRAM) - accounts = [ - AccountMeta(pubkey=WSOL_MINT, is_signer=False, is_writable=False), - AccountMeta(pubkey=TOKEN_PROGRAM, is_signer=False, is_writable=False), - AccountMeta(pubkey=creator, is_signer=False, is_writable=False), - AccountMeta(pubkey=creator_vault_authority, is_signer=False, is_writable=False), - AccountMeta(pubkey=creator_vault_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=creator_wsol_ata, is_signer=False, is_writable=True), - AccountMeta(pubkey=PUMP_SWAP_EVENT_AUTHORITY, is_signer=False, is_writable=False), - AccountMeta(pubkey=PUMP_AMM_PROGRAM, is_signer=False, is_writable=False), - ] +def build_migrate_v2_instruction( + *, + mint: Pubkey, + user: Pubkey, + withdraw_authority: Pubkey, + quote_mint: Pubkey = WSOL_MINT, + base_token_program: Pubkey = TOKEN_2022_PROGRAM, + quote_token_program: Pubkey = TOKEN_PROGRAM, +) -> Instruction: + """Move a complete curve's liquidity into its canonical PumpSwap pool. + `withdraw_authority` is read from pump.fun's Global account. + """ + bonding_curve = derive_bonding_curve(mint) + pool_authority = derive_pool_authority(mint) + pool = derive_amm_pool(mint, quote_mint) + lp_mint = derive_amm_lp_mint(pool) return Instruction( - program_id=PUMP_AMM_PROGRAM, - accounts=accounts, - data=COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR, + PUMP_PROGRAM, + MIGRATE_V2_DISCRIMINATOR, + [ + _meta(PUMP_GLOBAL), + _meta(withdraw_authority, writable=True), + _meta(mint), + _meta(quote_mint), + _meta(bonding_curve, writable=True), + _meta(find_ata(bonding_curve, mint, base_token_program), writable=True), + _meta(find_ata(bonding_curve, quote_mint, quote_token_program), writable=True), + _meta(user, writable=True, signer=True), + _meta(SYSTEM_PROGRAM), + _meta(PUMP_AMM_PROGRAM), + _meta(pool, writable=True), + _meta(pool_authority, writable=True), + _meta(find_ata(pool_authority, mint, base_token_program), writable=True), + _meta(find_ata(pool_authority, quote_mint, quote_token_program), writable=True), + _meta(PUMP_SWAP_GLOBAL_CONFIG), + _meta(lp_mint, writable=True), + _meta(find_ata(pool_authority, lp_mint, TOKEN_2022_PROGRAM), writable=True), + _meta(find_ata(pool, mint, base_token_program), writable=True), + _meta(find_ata(pool, quote_mint, quote_token_program), writable=True), + _meta(base_token_program), + _meta(quote_token_program), + _meta(TOKEN_2022_PROGRAM), + _meta(ASSOCIATED_TOKEN_PROGRAM), + _meta(PUMP_SWAP_EVENT_AUTHORITY), + _meta(RENT), + _meta(PUMP_EVENT_AUTHORITY), + _meta(PUMP_PROGRAM), + ], ) diff --git a/src/pumpfun_cli/protocol/pumpswap.py b/src/pumpfun_cli/protocol/pumpswap.py index b9ede52..cca55ff 100644 --- a/src/pumpfun_cli/protocol/pumpswap.py +++ b/src/pumpfun_cli/protocol/pumpswap.py @@ -1,30 +1,43 @@ """PumpSwap AMM pool discovery, parsing, and price calculation.""" +import random import struct from solders.pubkey import Pubkey -from spl.token.instructions import get_associated_token_address from pumpfun_cli.protocol.address import derive_amm_pool from pumpfun_cli.protocol.client import RpcClient from pumpfun_cli.protocol.contracts import ( - GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET, + DEFAULT_PUBKEY, + GLOBALCONFIG_BUYBACK_FEE_RECIPIENTS_OFFSET, + GLOBALCONFIG_COIN_CREATOR_FEE_BPS_OFFSET, + GLOBALCONFIG_LP_FEE_BPS_OFFSET, + GLOBALCONFIG_PROTOCOL_FEE_BPS_OFFSET, + GLOBALCONFIG_PROTOCOL_FEE_RECIPIENTS_OFFSET, + GLOBALCONFIG_RESERVED_FEE_RECIPIENT_OFFSET, + POOL_CASHBACK_OFFSET, + POOL_MAYHEM_MODE_OFFSET, + POOL_VIRTUAL_QUOTE_RESERVES_OFFSET, PUMP_SWAP_GLOBAL_CONFIG, - STANDARD_PUMPSWAP_FEE_RECIPIENT, + TOKEN_2022_PROGRAM, TOKEN_PROGRAM, WSOL_MINT, ) +from pumpfun_cli.protocol.quote import get_mint_info -async def get_pool_by_mint(client: RpcClient, mint: Pubkey) -> tuple[Pubkey, bytes]: - """Find the PumpSwap pool for a given token mint. +async def get_pool_by_mint( + client: RpcClient, mint: Pubkey, quote_mint: Pubkey = WSOL_MINT +) -> tuple[Pubkey, bytes]: + """Find the canonical PumpSwap pool a pump.fun coin graduated into. - Derives the pool address deterministically via PDA, then fetches - the account data with a single getAccountInfo call. + The address is derived from the mint and the curve's quote mint, then read + with a single getAccountInfo. - Returns (pool_address, pool_data) or raises RuntimeError if not found. + Raises: + RuntimeError: If the pool does not exist or cannot be read """ - pool_address = derive_amm_pool(mint) + pool_address = derive_amm_pool(mint, quote_mint) try: resp = await client.get_account_info(pool_address) except Exception as exc: @@ -34,100 +47,134 @@ async def get_pool_by_mint(client: RpcClient, mint: Pubkey) -> tuple[Pubkey, byt return pool_address, bytes(resp.value.data) +def _flag(data: bytes, offset: int) -> bool: + return len(data) > offset and bool(data[offset]) + + def parse_pool_data(data: bytes) -> dict: - """Parse binary pool account data into a dict. - - Layout (after 8-byte discriminator): - u8 pool_bump offset 8 - u16 index offset 9 - pub creator offset 11 - pub base_mint offset 43 - pub quote_mint offset 75 - pub lp_mint offset 107 - pub pool_base_token offset 139 - pub pool_quote_token offset 171 - u64 lp_supply offset 203 - pub coin_creator offset 211 + """Parse a PumpSwap Pool account. + + Layout, discriminator included: pool_bump u8 (8), index u16 (9), creator (11), + base_mint (43), quote_mint (75), lp_mint (107), pool_base_token_account + (139), pool_quote_token_account (171), lp_supply u64 (203), coin_creator + (211), is_mayhem_mode (243), is_cashback_coin (244), virtual_quote_reserves + i128 (245). + + `coin_creator` is set only on a canonical pool, one that graduated from a + pump.fun curve; every other pool carries the default pubkey. """ - pool_bump = data[8] - index = struct.unpack_from("= virtual_end + else 0 + ) coin_creator = Pubkey.from_bytes(data[211:243]) - return { - "pool_bump": pool_bump, - "index": index, - "creator": creator, - "base_mint": base_mint, - "quote_mint": quote_mint, - "lp_mint": lp_mint, - "pool_base_token_account": pool_base_token_account, - "pool_quote_token_account": pool_quote_token_account, - "lp_supply": lp_supply, + "pool_bump": data[8], + "index": struct.unpack_from(" tuple[Pubkey, Pubkey]: - """Determine fee recipient and its WSOL ATA. +def _pubkeys(data: bytes, offset: int, count: int) -> list[Pubkey]: + return [Pubkey.from_bytes(data[offset + 32 * i : offset + 32 * (i + 1)]) for i in range(count)] - Always reads the protocol_fee_recipient from the GlobalConfig account. - Falls back to the standard fee recipient if GlobalConfig is unavailable. - Returns (fee_recipient, fee_recipient_wsol_ata). +def parse_global_config(data: bytes) -> dict: + """Parse the PumpSwap GlobalConfig fields a trade needs.""" + return { + "lp_fee_bps": struct.unpack_from(" dict: + """Read and parse PumpSwap's GlobalConfig. + + Raises: + RuntimeError: If the account cannot be read """ resp = await client.get_account_info(PUMP_SWAP_GLOBAL_CONFIG) - if resp.value: - config_data = bytes(resp.value.data) - off = GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET - fee_recipient = Pubkey.from_bytes(config_data[off : off + 32]) - else: - fee_recipient = STANDARD_PUMPSWAP_FEE_RECIPIENT + if not resp.value: + raise RuntimeError("PumpSwap GlobalConfig account not found") + return parse_global_config(bytes(resp.value.data)) - fee_recipient_ata = get_associated_token_address(fee_recipient, WSOL_MINT, TOKEN_PROGRAM) - return fee_recipient, fee_recipient_ata + +def pick_fee_recipients(config: dict, is_mayhem_mode: bool) -> tuple[Pubkey, Pubkey]: + """(protocol fee recipient, buyback fee recipient) for one trade. + + A mayhem pool pays GlobalConfig's reserved recipient; any other pool pays + one of the eight protocol recipients, picked at random to spread contention. + """ + if is_mayhem_mode: + fee_recipient = config["reserved_fee_recipient"] + else: + fee_recipient = random.choice(config["protocol_fee_recipients"]) + return fee_recipient, random.choice(config["buyback_fee_recipients"]) async def get_token_program_id(client: RpcClient, mint: Pubkey) -> Pubkey: """Determine the token program (Token or Token2022) for a given mint.""" - from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM - resp = await client.get_account_info(mint) if not resp.value: raise RuntimeError(f"Mint account not found: {mint}") owner = resp.value.owner - if owner == TOKEN_PROGRAM: - return TOKEN_PROGRAM - if owner == TOKEN_2022_PROGRAM: - return TOKEN_2022_PROGRAM + if owner in (TOKEN_PROGRAM, TOKEN_2022_PROGRAM): + return owner raise RuntimeError(f"Unknown token program owner: {owner}") +async def resolve_pool_tokens(client: RpcClient, pool: dict) -> dict: + """Token programs and decimals for both sides of a pool. + + Non-canonical pools routinely carry 9-decimal base tokens, so neither side + is assumed. + """ + base_program, base_decimals = await get_mint_info(client, pool["base_mint"]) + quote_program, quote_decimals = await get_mint_info(client, pool["quote_mint"]) + return { + "base_token_program": base_program, + "base_decimals": base_decimals, + "quote_token_program": quote_program, + "quote_decimals": quote_decimals, + } + + async def get_pool_balances(client: RpcClient, pool: dict) -> tuple[int, int]: - """Fetch base and quote token balances from pool accounts. + """Base and effective quote reserves, raw units. - Returns (base_balance, quote_balance) as raw integers. + The quote side is the vault balance plus the pool's virtual quote reserves; + quoting off the vault alone under-prices any pool that carries some. """ base_balance = await client.get_token_account_balance(pool["pool_base_token_account"]) quote_balance = await client.get_token_account_balance(pool["pool_quote_token_account"]) - return base_balance, quote_balance - + return base_balance, quote_balance + pool.get("virtual_quote_reserves", 0) -async def get_pool_price(client: RpcClient, pool: dict) -> float: - """Calculate current token price in SOL from pool reserves. - Returns price as SOL per token (float). - """ - base_balance, quote_balance = await get_pool_balances(client, pool) - if base_balance == 0: +def pool_price( + base_balance: int, quote_balance: int, base_decimals: int, quote_decimals: int +) -> float: + """Price in whole quote units per whole base token.""" + if base_balance <= 0: return 0.0 - return quote_balance / base_balance + return (quote_balance / 10**quote_decimals) / (base_balance / 10**base_decimals) diff --git a/src/pumpfun_cli/protocol/quote.py b/src/pumpfun_cli/protocol/quote.py new file mode 100644 index 0000000..3eea70b --- /dev/null +++ b/src/pumpfun_cli/protocol/quote.py @@ -0,0 +1,248 @@ +"""Quote assets: the mint a coin is priced in, its token program and decimals. + +pump.fun prices coins in SOL, USDC and a list of other mints admitted by two +on-chain registries. Amounts on the wire are in the quote mint's raw units, so +its decimals are read from chain rather than assumed: a wrong power of ten +scales the price and the slippage cap in the same direction. +""" + +import struct +from typing import Any + +from solders.pubkey import Pubkey + +from pumpfun_cli.protocol.address import find_quote_control +from pumpfun_cli.protocol.client import RpcClient +from pumpfun_cli.protocol.contracts import ( + DEFAULT_PUBKEY, + KNOWN_QUOTE_MINTS, + MINT_DECIMALS_OFFSET, + PUMP_GLOBAL, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + USDC_MINT, + WSOL_MINT, +) + + +def normalize_quote_mint(quote_mint: Pubkey | None) -> Pubkey: + """Wrapped SOL for a SOL-paired coin's all-zero quote_mint, else the mint unchanged.""" + if quote_mint is None or quote_mint == DEFAULT_PUBKEY: + return WSOL_MINT + return quote_mint + + +def is_sol(quote_mint: Pubkey | None) -> bool: + return normalize_quote_mint(quote_mint) == WSOL_MINT + + +def quote_symbol(quote_mint: Pubkey) -> str: + """A short label for output: SOL, USDC, or the mint address for anything else.""" + if is_sol(quote_mint): + return "SOL" + if quote_mint == USDC_MINT: + return "USDC" + return str(quote_mint) + + +def parse_mint_account(owner: Pubkey, data: bytes) -> tuple[Pubkey, int]: + """Token program and decimals from a raw mint account. + + Raises: + ValueError: If the owner is not a token program or the data is too short + """ + if owner not in (TOKEN_PROGRAM, TOKEN_2022_PROGRAM): + raise ValueError(f"Account is owned by {owner}, not by a token program") + if len(data) <= MINT_DECIMALS_OFFSET: + raise ValueError(f"Account data is {len(data)} bytes, too short for a mint") + return owner, data[MINT_DECIMALS_OFFSET] + + +async def get_mint_info(client: RpcClient, mint: Pubkey) -> tuple[Pubkey, int]: + """Token program and decimals for any mint, from one getAccountInfo. + + WSOL and USDC are answered without an RPC call. + + Raises: + RuntimeError: If the mint does not exist or is not a token mint + """ + known = KNOWN_QUOTE_MINTS.get(mint) + if known is not None: + return known + resp = await client.get_account_info(mint) + if not resp.value: + raise RuntimeError(f"Mint account not found: {mint}") + try: + return parse_mint_account(resp.value.owner, bytes(resp.value.data)) + except ValueError as exc: + raise RuntimeError(f"{mint}: {exc}") from exc + + +# Global's fields in declaration order, walked rather than indexed, so a field +# added at the end leaves every earlier one readable. +_GLOBAL_LAYOUT: tuple[tuple[str, str], ...] = ( + ("initialized", "bool"), + ("authority", "pubkey"), + ("fee_recipient", "pubkey"), + ("initial_virtual_token_reserves", "u64"), + ("initial_virtual_sol_reserves", "u64"), + ("initial_real_token_reserves", "u64"), + ("token_total_supply", "u64"), + ("fee_basis_points", "u64"), + ("withdraw_authority", "pubkey"), + ("enable_migrate", "bool"), + ("pool_migration_fee", "u64"), + ("creator_fee_basis_points", "u64"), + ("fee_recipients", "pubkey[7]"), + ("set_creator_authority", "pubkey"), + ("admin_set_creator_authority", "pubkey"), + ("create_v2_enabled", "bool"), + ("whitelist_pda", "pubkey"), + ("reserved_fee_recipient", "pubkey"), + ("mayhem_mode_enabled", "bool"), + ("reserved_fee_recipients", "pubkey[7]"), + ("is_cashback_enabled", "bool"), + ("buyback_fee_recipients", "pubkey[8]"), + ("buyback_basis_points", "u64"), + ("initial_virtual_quote_reserves", "u64"), + ("whitelisted_quote_mints", "pubkey[1]"), + ("creator_fee_configurable", "bool"), + ("max_configurable_creator_fee_bps", "u64"), + ("holder_reward_claim_authority", "pubkey"), + ("is_holder_reward_enabled", "bool"), +) +_GLOBAL_SIZES = {"bool": 1, "u64": 8, "pubkey": 32} + + +def decode_global(data: bytes) -> dict[str, Any]: + """Decode pump.fun's Global account, stopping at the last field the data holds.""" + fields: dict[str, Any] = {} + offset = 8 + for name, kind in _GLOBAL_LAYOUT: + base, _, count = kind.partition("[") + repeat = int(count.rstrip("]")) if count else 1 + width = _GLOBAL_SIZES[base] * repeat + if offset + width > len(data): + break + chunk = data[offset : offset + width] + if base == "bool": + fields[name] = bool(chunk[0]) + elif base == "u64": + fields[name] = struct.unpack(" dict[str, Any]: + """Read and decode pump.fun's Global account. + + Raises: + RuntimeError: If the account cannot be read + """ + resp = await client.get_account_info(PUMP_GLOBAL) + if not resp.value: + raise RuntimeError("pump.fun Global account not found") + return decode_global(bytes(resp.value.data)) + + +# QuoteControl: discriminator, admin (32), 64 reserved bytes, then a vec of +# (mint, initial_virtual_quote_reserves u64). +_QUOTE_CONTROL_MINTS_OFFSET = 8 + 32 + 64 +_QUOTE_CONTROL_ENTRY_SIZE = 32 + 8 + + +def decode_quote_control(data: bytes) -> dict[Pubkey, int]: + """Decode QuoteControl into quote mint -> opening virtual quote reserves (raw units). + + Raises: + ValueError: If the account is too short to hold the vec length + """ + if len(data) < _QUOTE_CONTROL_MINTS_OFFSET + 4: + raise ValueError(f"QuoteControl account is only {len(data)} bytes") + count = struct.unpack_from(" len(data): + break + entries[Pubkey.from_bytes(data[at : at + 32])] = struct.unpack_from(" dict[Pubkey, int]: + """Read and decode the QuoteControl registry. + + Raises: + RuntimeError: If the account cannot be read + """ + resp = await client.get_account_info(find_quote_control()) + if not resp.value: + raise RuntimeError("pump.fun QuoteControl account not found") + return decode_quote_control(bytes(resp.value.data)) + + +def opening_quote_reserves( + registry: dict[Pubkey, int], quote_mint: Pubkey, global_state: dict[str, Any] +) -> int: + """Opening virtual quote reserves for a new coin priced in `quote_mint`. + + SOL uses Global's SOL figure, a QuoteControl mint carries its own, and a + mint only Global whitelists (USDC) uses Global's quote figure. The figures + differ by orders of magnitude, so an unlisted mint is refused, not defaulted. + + Raises: + ValueError: If neither registry admits the mint + """ + if is_sol(quote_mint): + return global_state["initial_virtual_sol_reserves"] + reserves = registry.get(quote_mint) + if reserves is not None: + return reserves + if quote_mint in (global_state.get("whitelisted_quote_mints") or []): + return global_state["initial_virtual_quote_reserves"] + raise ValueError(f"pump.fun does not admit {quote_mint} as a quote asset.") + + +def check_creator_fee( + global_state: dict[str, Any], + registry: dict[Pubkey, int], + quote_mint: Pubkey, + creator_fee_bps: int | None, +) -> None: + """Refuse a creator fee the program would reject or silently store as zero. + + The program applies a creator fee only on a coin priced in a mint that + QuoteControl admits. On SOL, or on USDC, it accepts the argument and stores 0. + + Raises: + ValueError: If the fee cannot take effect on this coin + """ + if not creator_fee_bps: + return + if not global_state.get("creator_fee_configurable"): + raise ValueError("pump.fun is not accepting a configurable creator fee right now.") + ceiling = global_state.get("max_configurable_creator_fee_bps", 0) + if creator_fee_bps > ceiling: + raise ValueError( + f"Creator fee {creator_fee_bps} bps is above the current maximum of {ceiling} bps." + ) + if quote_mint not in registry: + raise ValueError( + f"A creator fee only applies to a coin priced in a QuoteControl mint, and " + f"{quote_symbol(quote_mint)} is not one; pump.fun would store 0." + ) + + +def check_mayhem_quote(quote_mint: Pubkey, is_mayhem_mode: bool) -> None: + """Refuse a mayhem coin priced in anything but SOL (the program rejects it with 6071). + + Raises: + ValueError: If a mayhem coin is paired with a non-SOL quote asset + """ + if is_mayhem_mode and not is_sol(quote_mint): + raise ValueError("Mayhem mode can only be used with SOL as the quote asset.") diff --git a/tests/test_commands/test_human_output.py b/tests/test_commands/test_human_output.py new file mode 100644 index 0000000..22a4b6a --- /dev/null +++ b/tests/test_commands/test_human_output.py @@ -0,0 +1,219 @@ +"""Human-readable output for the reshaped results, labelled by the coin's quote asset.""" + +from unittest.mock import AsyncMock, patch + +import pytest +from solders.keypair import Keypair +from typer.testing import CliRunner + +from pumpfun_cli.cli import app +from pumpfun_cli.crypto import encrypt_keypair + +runner = CliRunner() +MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + + +@pytest.fixture +def wallet(tmp_path, monkeypatch): + monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path)) + monkeypatch.setenv("PUMPFUN_PASSWORD", "testpass") + config_dir = tmp_path / "pumpfun-cli" + config_dir.mkdir() + encrypt_keypair(Keypair(), "testpass", config_dir / "wallet.enc") + + +def _human(module: str): + return patch(f"pumpfun_cli.commands.{module}.render", return_value=False) + + +def test_buy_and_sell_name_the_quote_asset(wallet): + with ( + _human("trade"), + patch("pumpfun_cli.commands.trade.buy_token", new_callable=AsyncMock) as buy, + patch("pumpfun_cli.commands.trade.sell_token", new_callable=AsyncMock) as sell, + ): + buy.return_value = { + "action": "buy", + "quote_symbol": "USDC", + "quote_spent": 5.0, + "tokens_received": 10.0, + "explorer": "x", + } + sell.return_value = { + "action": "sell", + "quote_symbol": "USDC", + "quote_received": 4.5, + "tokens_sold": 10.0, + "explorer": "x", + } + bought = runner.invoke(app, ["--rpc", "http://rpc", "buy", MINT, "5"]) + sold = runner.invoke(app, ["--rpc", "http://rpc", "sell", MINT, "all"]) + assert bought.exit_code == 0 and "for 5.0 USDC" in bought.output + assert sold.exit_code == 0 and "4.500000 USDC" in sold.output + + +def test_dry_runs(wallet): + common = { + "dry_run": True, + "venue": "bonding_curve", + "quote_symbol": "SOL", + "effective_price": 1e-8, + "spot_price": 1e-8, + "price_impact_pct": 0.5, + "slippage_pct": 15, + } + with ( + _human("trade"), + patch("pumpfun_cli.commands.trade.buy_token", new_callable=AsyncMock) as buy, + patch("pumpfun_cli.commands.trade.sell_token", new_callable=AsyncMock) as sell, + ): + buy.return_value = { + **common, + "action": "buy", + "quote_in": 0.01, + "expected_tokens": 1.0, + "min_tokens_out": 0.9, + } + sell.return_value = { + **common, + "action": "sell", + "tokens_in": 1.0, + "expected_quote": 0.01, + "min_quote_out": 0.009, + } + b = runner.invoke(app, ["--rpc", "http://rpc", "buy", "--dry-run", MINT, "0.01"]) + s = runner.invoke(app, ["--rpc", "http://rpc", "sell", "--dry-run", MINT, "1"]) + assert b.exit_code == 0 and "0.01 SOL" in b.output + assert s.exit_code == 0 and "SOL/token" in s.output + + +def test_collect_and_claim_forward_quote_mint(wallet): + with ( + _human("trade"), + patch( + "pumpfun_cli.commands.trade.collect_creator_fees_core", new_callable=AsyncMock + ) as col, + patch("pumpfun_cli.commands.trade.claim_cashback_core", new_callable=AsyncMock) as claim, + ): + col.return_value = { + "quote_symbol": "USDC", + "pump_recovered": 1.0, + "pumpswap_recovered": 0.0, + "explorer": "x", + } + claim.return_value = {"action": "claim_cashback", "explorer": "x"} + c = runner.invoke( + app, ["--rpc", "http://rpc", "collect-creator-fee", "--quote-mint", MINT] + ) + k = runner.invoke(app, ["--rpc", "http://rpc", "claim-cashback", "--quote-mint", MINT]) + assert c.exit_code == 0 and "1.000000 USDC" in c.output + assert col.call_args.args[3] == MINT + assert k.exit_code == 0 and claim.call_args.args[3] == MINT + + +def test_info(wallet): + with ( + _human("info"), + patch("pumpfun_cli.commands.info.get_token_info", new_callable=AsyncMock) as info, + ): + info.return_value = { + "mint": MINT, + "quote_symbol": "USDC", + "price": 0.5, + "bonding_progress": 10.0, + "graduated": False, + "real_quote_reserves": 3.0, + "creator": "c", + "is_mayhem_mode": False, + "is_cashback_coin": False, + "is_holder_reward": True, + "creator_fee_bps": 300, + } + result = runner.invoke(app, ["--rpc", "http://rpc", "info", MINT]) + assert result.exit_code == 0 + assert "0.5000000000 USDC" in result.output + assert "Holder rewards: Yes" in result.output + assert "300 bps" in result.output + + +def test_launch_forwards_options(wallet): + with ( + _human("launch"), + patch("pumpfun_cli.commands.launch.launch_token", new_callable=AsyncMock) as launch, + ): + launch.return_value = { + "name": "N", + "ticker": "T", + "mint": MINT, + "quote_symbol": "USDC", + "explorer": "x", + "pump_url": "y", + "initial_buy": 2.0, + "is_holder_reward": True, + "creator_fee_bps": 100, + } + result = runner.invoke( + app, + [ + "--rpc", + "http://rpc", + "launch", + "--name", + "N", + "--ticker", + "T", + "--desc", + "d", + "--buy", + "2", + "--quote-mint", + MINT, + "--creator-fee-bps", + "100", + "--holder-reward", + ], + ) + assert result.exit_code == 0, result.output + kwargs = launch.call_args.kwargs + assert kwargs["quote_mint_str"] == MINT + assert kwargs["creator_fee_bps"] == 100 + assert kwargs["is_holder_reward"] is True + assert kwargs["initial_buy"] == 2.0 + assert "Buy: 2.0 USDC" in result.output + + +def test_launch_rejects_negative_fee(wallet): + result = runner.invoke( + app, + [ + "--rpc", + "http://rpc", + "launch", + "--name", + "N", + "--ticker", + "T", + "--desc", + "d", + "--creator-fee-bps", + "-1", + ], + ) + assert result.exit_code != 0 + + +@pytest.mark.parametrize("amount", ["0", "-1"]) +def test_buy_rejects_non_positive_amount(wallet, amount): + with patch("pumpfun_cli.commands.trade.buy_token", new_callable=AsyncMock) as buy: + result = runner.invoke(app, ["--rpc", "http://rpc", "buy", MINT, "--", amount]) + assert result.exit_code == 2 + buy.assert_not_called() + + +def test_launch_rejects_negative_buy(wallet): + with patch("pumpfun_cli.commands.launch.launch_token", new_callable=AsyncMock) as launch: + result = runner.invoke( + app, ["--rpc", "http://rpc", "launch", "--name", "N", "--ticker", "T", "--buy", "-1"] + ) + assert result.exit_code != 0 + launch.assert_not_called() diff --git a/tests/test_commands/test_smoke.py b/tests/test_commands/test_smoke.py index 2b2b16c..893e441 100644 --- a/tests/test_commands/test_smoke.py +++ b/tests/test_commands/test_smoke.py @@ -173,12 +173,14 @@ def test_tokens_no_subcommand_shows_help(): assert result.exit_code == 0 -def test_launch_help_shows_cashback(): - """Launch --help includes --cashback flag.""" +def test_launch_help_shows_quote_options(): + """Launch --help lists the create_v2 options and no longer offers cashback.""" result = runner.invoke(app, ["launch", "--help"]) assert result.exit_code == 0 out = _strip_ansi(result.output) - assert "--cashback" in out + for flag in ("--quote-mint", "--creator-fee-bps", "--holder-reward", "--slippage"): + assert flag in out + assert "--cashback" not in out @pytest.mark.parametrize("limit", ["0", "-1"]) diff --git a/tests/test_commands/test_trade_cmd.py b/tests/test_commands/test_trade_cmd.py index f8e7561..c47eaf6 100644 --- a/tests/test_commands/test_trade_cmd.py +++ b/tests/test_commands/test_trade_cmd.py @@ -83,7 +83,8 @@ def test_buy_graduated_fallback_json(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -228,7 +229,8 @@ def test_sell_graduated_fallback_json(tmp_path, monkeypatch): "action": "sell", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_received": 0.01, + "quote_symbol": "SOL", + "quote_received": 0.01, "tokens_sold": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -282,7 +284,8 @@ def test_buy_priority_fee_flag_forwarded(tmp_path, monkeypatch): mock_buy.return_value = { "action": "buy", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -329,10 +332,11 @@ def test_buy_dry_run_flag(tmp_path, monkeypatch): "action": "buy", "venue": "bonding_curve", "mint": _FAKE_MINT, - "sol_in": 0.01, + "quote_symbol": "SOL", + "quote_in": 0.01, "expected_tokens": 1234.56, - "effective_price_sol": 0.0000081, - "spot_price_sol": 0.000008, + "effective_price": 0.0000081, + "spot_price": 0.000008, "price_impact_pct": 1.25, "min_tokens_out": 1049.87, "slippage_pct": 15, @@ -372,11 +376,12 @@ def test_sell_dry_run_flag(tmp_path, monkeypatch): "venue": "bonding_curve", "mint": _FAKE_MINT, "tokens_in": 1000.0, - "expected_sol": 0.082, - "effective_price_sol": 0.000082, - "spot_price_sol": 0.000083, + "quote_symbol": "SOL", + "expected_quote": 0.082, + "effective_price": 0.000082, + "spot_price": 0.000083, "price_impact_pct": -1.2, - "min_sol_out": 0.0697, + "min_quote_out": 0.0697, "slippage_pct": 15, } @@ -504,7 +509,8 @@ def test_buy_slippage_zero(tmp_path, monkeypatch): mock_buy.return_value = { "action": "buy", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -536,7 +542,8 @@ def test_buy_slippage_100(tmp_path, monkeypatch): mock_buy.return_value = { "action": "buy", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -568,7 +575,8 @@ def test_sell_slippage_zero(tmp_path, monkeypatch): mock_sell.return_value = { "action": "sell", "mint": _FAKE_MINT, - "sol_received": 0.01, + "quote_symbol": "SOL", + "quote_received": 0.01, "tokens_sold": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -600,7 +608,8 @@ def test_sell_slippage_100(tmp_path, monkeypatch): mock_sell.return_value = { "action": "sell", "mint": _FAKE_MINT, - "sol_received": 0.01, + "quote_symbol": "SOL", + "quote_received": 0.01, "tokens_sold": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -726,7 +735,8 @@ def test_buy_json_output_has_expected_keys(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -750,7 +760,8 @@ def test_buy_json_output_has_expected_keys(tmp_path, monkeypatch): "action", "venue", "mint", - "sol_spent", + "quote_symbol", + "quote_spent", "tokens_received", "signature", "explorer", @@ -788,7 +799,8 @@ def test_buy_graduated_fallback_forwards_slippage(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -817,7 +829,8 @@ def test_sell_graduated_fallback_forwards_slippage(tmp_path, monkeypatch): "action": "sell", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_received": 0.01, + "quote_symbol": "SOL", + "quote_received": 0.01, "tokens_sold": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -846,7 +859,8 @@ def test_buy_graduated_fallback_forwards_priority_fee(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -888,10 +902,11 @@ def test_buy_graduated_fallback_forwards_dry_run(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_in": 0.01, + "quote_symbol": "SOL", + "quote_in": 0.01, "expected_tokens": 100.0, - "effective_price_sol": 0.0001, - "spot_price_sol": 0.00009, + "effective_price": 0.0001, + "spot_price": 0.00009, "price_impact_pct": 1.0, "min_tokens_out": 95.0, "slippage_pct": 15, @@ -925,11 +940,12 @@ def test_sell_graduated_fallback_forwards_dry_run(tmp_path, monkeypatch): "venue": "pumpswap", "mint": _FAKE_MINT, "tokens_in": 100.0, - "expected_sol": 0.01, - "effective_price_sol": 0.0001, - "spot_price_sol": 0.00009, + "quote_symbol": "SOL", + "expected_quote": 0.01, + "effective_price": 0.0001, + "spot_price": 0.00009, "price_impact_pct": -1.0, - "min_sol_out": 0.0085, + "min_quote_out": 0.0085, "slippage_pct": 15, } @@ -957,7 +973,8 @@ def test_buy_graduated_fallback_forwards_confirm(tmp_path, monkeypatch): "action": "buy", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_spent": 0.01, + "quote_symbol": "SOL", + "quote_spent": 0.01, "tokens_received": 100.0, "signature": "sig", "explorer": "https://solscan.io/tx/sig", @@ -1048,7 +1065,8 @@ def test_sell_graduated_fallback_sell_all(tmp_path, monkeypatch): "action": "sell", "venue": "pumpswap", "mint": _FAKE_MINT, - "sol_received": 0.05, + "quote_symbol": "SOL", + "quote_received": 0.05, "tokens_sold": 500.0, "signature": "sellall_sig", "explorer": "https://solscan.io/tx/sellall_sig", diff --git a/tests/test_core/helpers.py b/tests/test_core/helpers.py index 230a9ab..64988a5 100644 --- a/tests/test_core/helpers.py +++ b/tests/test_core/helpers.py @@ -1,35 +1,275 @@ -"""Shared test helpers for core tests.""" +"""Shared test helpers: real account layouts and an in-memory RPC client.""" import struct +from types import SimpleNamespace from solders.pubkey import Pubkey +from pumpfun_cli.protocol.address import ( + derive_amm_pool, + derive_bonding_curve, + find_quote_control, +) +from pumpfun_cli.protocol.contracts import ( + BONDING_CURVE_DISCRIMINATOR, + DEFAULT_PUBKEY, + GLOBAL_CONFIG_DISCRIMINATOR, + GLOBALCONFIG_BUYBACK_FEE_RECIPIENTS_OFFSET, + GLOBALCONFIG_COIN_CREATOR_FEE_BPS_OFFSET, + GLOBALCONFIG_LP_FEE_BPS_OFFSET, + GLOBALCONFIG_PROTOCOL_FEE_BPS_OFFSET, + GLOBALCONFIG_PROTOCOL_FEE_RECIPIENTS_OFFSET, + GLOBALCONFIG_RESERVED_FEE_RECIPIENT_OFFSET, + POOL_DISCRIMINATOR, + PUMP_GLOBAL, + PUMP_SWAP_GLOBAL_CONFIG, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + WSOL_MINT, +) +from pumpfun_cli.protocol.quote import _GLOBAL_LAYOUT + +MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") +COIN = Pubkey.from_string("9PWeFRMCgEpt6ygn4L6c2sEFtRTerppBKUTWgyRwpump") +CREATOR = Pubkey.from_string("7BajYqDdCgin9T6UnJon1pH2kJwXhLe9d277iRcva8wX") +T22_QUOTE = Pubkey.from_string("SPCXxcqXj6e5dJDVNovHN8744zkbhM2bYudU45BimGb") + +PROTOCOL_RECIPIENTS = [Pubkey.new_unique() for _ in range(8)] +RESERVED_RECIPIENT = Pubkey.new_unique() +BUYBACK_RECIPIENTS = [Pubkey.new_unique() for _ in range(8)] + + +def build_curve_data( + *, + virtual_token: int = 1_000_000_000_000_000, + virtual_quote: int = 30_000_000_000, + real_token: int = 793_100_000_000_000, + real_quote: int = 0, + supply: int = 1_000_000_000_000_000, + complete: bool = False, + creator: Pubkey = CREATOR, + mayhem: bool = False, + cashback: bool = False, + quote_mint: Pubkey = DEFAULT_PUBKEY, + creator_fee_bps: int = 0, + holder_reward: bool = False, + length: int = 125, +) -> bytes: + """A BondingCurve account as create_v2 allocates it (125 bytes) or longer.""" + data = bytearray(max(length, 125)) + data[:8] = BONDING_CURVE_DISCRIMINATOR + struct.pack_into( + " bytes: - """Build minimal synthetic PumpSwap pool data for mocking. - - Returns a 243-byte blob matching the on-chain pool account layout. - """ - _creator = bytes(creator or Pubkey.from_string("11111111111111111111111111111112")) - _mint = bytes(mint or Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")) - wsol = bytes(Pubkey.from_string("So11111111111111111111111111111111111111112")) - filler = bytes(Pubkey.from_string("11111111111111111111111111111113")) - - data = bytearray(243) - data[0:8] = b"\x00" * 8 # discriminator - data[8] = 255 # pool_bump + """A PumpSwap Pool account (301 bytes, like live pools).""" + data = bytearray(301) + data[:8] = POOL_DISCRIMINATOR + data[8] = 255 struct.pack_into(" bytes: + """A PumpSwap GlobalConfig with known fees and recipients.""" + data = bytearray(GLOBALCONFIG_BUYBACK_FEE_RECIPIENTS_OFFSET + 8 * 32 + 16) + data[:8] = GLOBAL_CONFIG_DISCRIMINATOR + struct.pack_into(" bytes: + """pump.fun's Global account, every field in declaration order.""" + values = {**GLOBAL_DEFAULTS, **overrides} + out = bytearray(b"\x00" * 8) + for name, kind in _GLOBAL_LAYOUT: + base, _, count = kind.partition("[") + repeat = int(count.rstrip("]")) if count else 1 + value = values.get(name) + if base == "bool": + out += bytes([1 if value else 0]) + elif base == "u64": + out += struct.pack(" bytes: + data = bytearray(8 + 32 + 64) + data += struct.pack(" bytes: + data = bytearray(82) + data[44] = decimals return bytes(data) + + +class FakeClient: + """In-memory stand-in for RpcClient: accounts by address, sends recorded.""" + + def __init__(self): + self.accounts: dict[Pubkey, SimpleNamespace] = {} + self.balances: dict[Pubkey, int] = {} + self.token_balances: dict[Pubkey, int] = {} + self.default_balance = 10_000_000_000 + self.sent: list[dict] = [] + self.send_error: Exception | None = None + + def add(self, address: Pubkey, data: bytes, owner: Pubkey = TOKEN_PROGRAM) -> None: + self.accounts[address] = SimpleNamespace(data=data, owner=owner, lamports=1) + + def add_mint(self, mint: Pubkey, decimals: int, program: Pubkey = TOKEN_2022_PROGRAM) -> None: + self.add(mint, build_mint_data(decimals), owner=program) + + async def get_account_info(self, pubkey: Pubkey): + return SimpleNamespace(value=self.accounts.get(pubkey)) + + async def get_multiple_accounts(self, pubkeys): + return [self.accounts.get(pk) for pk in pubkeys] + + async def get_balance(self, pubkey: Pubkey, commitment=None) -> int: + return self.balances.get(pubkey, self.default_balance) + + async def get_minimum_balance_for_rent_exemption(self, size: int) -> int: + return 650_240 if size == 0 else (128 + size) * 5_080 + + async def get_token_account_balance(self, account: Pubkey) -> int: + return self.token_balances.get(account, 0) + + async def send_tx( + self, instructions, signers, compute_units=100_000, priority_fee=200_000, confirm=False + ): + if self.send_error: + raise self.send_error + self.sent.append( + { + "instructions": instructions, + "signers": signers, + "compute_units": compute_units, + "priority_fee": priority_fee, + "confirm": confirm, + } + ) + return "fakesig" + + async def close(self) -> None: + return None + + +def chain_with_coin( + *, + mint: Pubkey = COIN, + curve: bytes | None = None, + quote_decimals: int | None = None, + quote_mint: Pubkey | None = None, + quote_program: Pubkey = TOKEN_2022_PROGRAM, +) -> FakeClient: + """A chain holding one pump.fun coin's mint, its curve, and Global.""" + client = FakeClient() + client.add_mint(mint, 6, TOKEN_2022_PROGRAM) + client.add(derive_bonding_curve(mint), curve if curve is not None else build_curve_data()) + client.add(PUMP_GLOBAL, build_global_data()) + if quote_mint is not None and quote_decimals is not None: + client.add_mint(quote_mint, quote_decimals, quote_program) + return client + + +def add_pool( + client: FakeClient, + *, + mint: Pubkey = COIN, + quote_mint: Pubkey = WSOL_MINT, + base_reserve: int = 500_000_000_000_000, + quote_reserve: int = 80_000_000_000, + **pool_kwargs, +) -> dict: + """Add a canonical pool for `mint`, its vault balances and GlobalConfig.""" + base_account, quote_account = Pubkey.new_unique(), Pubkey.new_unique() + pool_address = derive_amm_pool(mint, quote_mint) + client.add( + pool_address, + build_pool_data( + mint=mint, + quote_mint=quote_mint, + base_account=base_account, + quote_account=quote_account, + **pool_kwargs, + ), + owner=Pubkey.new_unique(), + ) + client.token_balances[base_account] = base_reserve + client.token_balances[quote_account] = quote_reserve + client.add(PUMP_SWAP_GLOBAL_CONFIG, build_global_config_data(), owner=Pubkey.new_unique()) + return {"address": pool_address, "base_account": base_account, "quote_account": quote_account} + + +def add_quote_control(client: FakeClient, entries: dict[Pubkey, int]) -> None: + client.add(find_quote_control(), build_quote_control_data(entries), owner=Pubkey.new_unique()) diff --git a/tests/test_core/test_amounts.py b/tests/test_core/test_amounts.py new file mode 100644 index 0000000..8b7b3fa --- /dev/null +++ b/tests/test_core/test_amounts.py @@ -0,0 +1,26 @@ +"""User amounts become raw units exactly, never through a float product.""" + +import pytest + +from pumpfun_cli.core.amounts import to_raw + + +@pytest.mark.parametrize( + "amount,decimals,raw", + [ + (0.29, 9, 290_000_000), + (0.57, 2, 57), + (4.35, 6, 4_350_000), + ("0.29", 2, 29), + ("1.5", 6, 1_500_000), + (1e-9, 9, 1), + ("0.0000001", 6, 0), + (0, 9, 0), + ], +) +def test_to_raw(amount, decimals, raw): + assert to_raw(amount, decimals) == raw + + +def test_float_product_would_have_lost_a_unit(): + assert int(0.57 * 10**2) == 56 diff --git a/tests/test_core/test_auto_routing.py b/tests/test_core/test_auto_routing.py index 1b3ccef..98c8706 100644 --- a/tests/test_core/test_auto_routing.py +++ b/tests/test_core/test_auto_routing.py @@ -1,376 +1,59 @@ -"""Tests for auto-routing: graduated tokens fall back to PumpSwap. +"""The pieces auto-routing is built from: a graduated curve hands off to its pool. -These tests verify the individual core functions (buy_token returns graduated, -buy_pumpswap succeeds independently) rather than the command-layer wiring. -The auto-routing logic lives in commands/trade.py, so here we test the -building blocks that make it work. +`commands/trade.py` retries on PumpSwap when the curve reports `graduated`; +these check that each side of that hand-off behaves. """ -from unittest.mock import AsyncMock, MagicMock, patch - -import pytest +from unittest.mock import patch from pumpfun_cli.core.pumpswap import buy_pumpswap, sell_pumpswap from pumpfun_cli.core.trade import buy_token, sell_token -from pumpfun_cli.protocol.contracts import ( - GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET, - STANDARD_PUMPSWAP_FEE_RECIPIENT, - TOKEN_2022_PROGRAM, - TOKEN_PROGRAM, -) -from tests.test_core.helpers import build_pool_data - -_PATCH_TOKEN_PROG = patch( - "pumpfun_cli.core.trade.get_token_program_id", - new=AsyncMock(return_value=TOKEN_2022_PROGRAM), -) - -_VALID_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - - -# --- helpers (reused from test_pumpswap.py) --- - - -def _mock_global_config_resp(): - off = GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET - config_data = bytearray(off + 32) - config_data[off : off + 32] = bytes(STANDARD_PUMPSWAP_FEE_RECIPIENT) - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = bytes(config_data) - return resp - - -def _mock_pool_resp(pool_data): - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = pool_data - return resp - - -def _mock_pool_not_found(): - resp = MagicMock() - resp.value = None - return resp - - -def _mock_token_program_resp(): - resp = MagicMock() - resp.value = MagicMock() - resp.value.owner = TOKEN_PROGRAM - return resp - - -def _mock_vol_accumulator_resp(): - resp = MagicMock() - resp.value = None - return resp - - -def _mock_bonding_curve_graduated(): - """Build a mock bonding curve response where complete=True.""" - resp = MagicMock() - resp.value = MagicMock() - # We patch IDLParser to return graduated state, so data doesn't matter - resp.value.data = b"\x00" * 200 - return resp - - -def _mock_bonding_curve_not_found(): - resp = MagicMock() - resp.value = None - return resp - - -# --- buy auto-routing tests --- - - -@pytest.mark.asyncio -@_PATCH_TOKEN_PROG -async def test_buy_auto_route_graduated_to_pumpswap(tmp_keystore): - """buy_token returns graduated, then buy_pumpswap succeeds with venue==pumpswap.""" - # Step 1: buy_token detects graduated - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_bonding_curve_graduated() - client.close = AsyncMock() - - with patch("pumpfun_cli.core.trade.IDLParser") as MockIDL: - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = {"complete": True} - - result = await buy_token("http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01) - - assert result["error"] == "graduated" - - # Step 2: buy_pumpswap succeeds - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "pumpswap_sig" - client.close = AsyncMock() - - ps_result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01) - - assert ps_result["venue"] == "pumpswap" - assert ps_result["action"] == "buy" - assert ps_result["signature"] == "pumpswap_sig" - - -@pytest.mark.asyncio -@_PATCH_TOKEN_PROG -async def test_sell_auto_route_graduated_to_pumpswap(tmp_keystore): - """sell_token returns graduated, then sell_pumpswap succeeds with venue==pumpswap.""" - # Step 1: sell_token detects graduated - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_bonding_curve_graduated() - client.close = AsyncMock() - - with patch("pumpfun_cli.core.trade.IDLParser") as MockIDL: - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = {"complete": True} - - result = await sell_token("http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all") - - assert result["error"] == "graduated" - - # Step 2: sell_pumpswap succeeds - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [ - 1_000_000, - 1_000_000_000, - 30_000_000_000, - ] - client.send_tx.return_value = "sellsig_ps" - client.close = AsyncMock() - - ps_result = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all") - - assert ps_result["venue"] == "pumpswap" - assert ps_result["action"] == "sell" - assert ps_result["signature"] == "sellsig_ps" - - -@pytest.mark.asyncio -async def test_buy_auto_route_forwards_slippage(tmp_keystore): - """slippage=5 is forwarded to buy_pumpswap.""" - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "buysig" - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01, slippage=5 - ) - - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" - # With slippage=5, min_base_amount_out should be 95% of estimated - # (verified by the fact that the trade succeeded with slippage=5) - - -@pytest.mark.asyncio -async def test_sell_auto_route_forwards_slippage(tmp_keystore): - """slippage=5 is forwarded to sell_pumpswap.""" - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [ - 1_000_000, - 1_000_000_000, - 30_000_000_000, - ] - client.send_tx.return_value = "sellsig" - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all", slippage=5 - ) - - assert result["action"] == "sell" - assert result["venue"] == "pumpswap" - - -@pytest.mark.asyncio -async def test_buy_auto_route_forwards_dry_run(tmp_keystore): - """dry_run=True forwarded to buy_pumpswap, send_tx not called.""" - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01, dry_run=True - ) - - assert result["dry_run"] is True - assert result["venue"] == "pumpswap" - assert "signature" not in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_sell_auto_route_forwards_dry_run(tmp_keystore): - """dry_run=True forwarded to sell_pumpswap, send_tx not called.""" - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [ - 1_000_000, - 1_000_000_000, - 30_000_000_000, - ] - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all", dry_run=True - ) - - assert result["dry_run"] is True - assert result["venue"] == "pumpswap" - assert "signature" not in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_buy_auto_route_sell_all_through_fallback(tmp_keystore): - """sell_pumpswap with amount_str='all' works after graduated fallback.""" - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [ - 500_000_000, # user balance - 1_000_000_000, # pool base - 30_000_000_000, # pool quote - ] - client.send_tx.return_value = "sell_all_sig" - client.close = AsyncMock() - - result = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all") - - assert result["action"] == "sell" - assert result["venue"] == "pumpswap" - assert result["signature"] == "sell_all_sig" - assert result["tokens_sold"] > 0 - - -@pytest.mark.asyncio -@_PATCH_TOKEN_PROG -async def test_buy_not_found_does_not_trigger_fallback(tmp_keystore): - """buy_token returns not_found — no pumpswap call should follow.""" - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_bonding_curve_not_found() - client.close = AsyncMock() - - result = await buy_token("http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01) - - assert result["error"] == "not_found" - # The command layer checks: if result.get("error") == "graduated" - # "not_found" != "graduated" so pumpswap would NOT be called. - assert result["error"] != "graduated" - - -@pytest.mark.asyncio -@_PATCH_TOKEN_PROG -async def test_sell_not_found_does_not_trigger_fallback(tmp_keystore): - """sell_token returns not_found — no pumpswap call should follow.""" - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_bonding_curve_not_found() - client.close = AsyncMock() - - result = await sell_token("http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all") - +from pumpfun_cli.protocol.address import derive_bonding_curve, find_ata +from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM +from tests.test_core.helpers import COIN, add_pool, build_curve_data, chain_with_coin + +MINT_STR = str(COIN) + + +def _both(client): + return ( + patch("pumpfun_cli.core.trade.RpcClient", return_value=client), + patch("pumpfun_cli.core.pumpswap.RpcClient", return_value=client), + ) + + +async def test_graduated_buy_hands_off(tmp_keystore): + client = chain_with_coin(curve=build_curve_data(complete=True, virtual_quote=0)) + add_pool(client) + a, b = _both(client) + with a, b: + curve = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + amm = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert curve["error"] == "graduated" + assert amm["venue"] == "pumpswap" and amm["signature"] == "fakesig" + + +async def test_graduated_sell_hands_off(tmp_keystore): + from pathlib import Path + + from pumpfun_cli.crypto import decrypt_keypair + + user = decrypt_keypair(Path(tmp_keystore), "testpass").pubkey() + client = chain_with_coin(curve=build_curve_data(complete=True, virtual_quote=0)) + add_pool(client) + client.token_balances[find_ata(user, COIN, TOKEN_2022_PROGRAM)] = 1_000_000 + a, b = _both(client) + with a, b: + curve = await sell_token("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + amm = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + assert curve["error"] == "graduated" + assert amm["venue"] == "pumpswap" and amm["tokens_sold"] == 1.0 + + +async def test_missing_curve_is_not_graduated(tmp_keystore): + client = chain_with_coin() + del client.accounts[derive_bonding_curve(COIN)] + a, b = _both(client) + with a, b: + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) assert result["error"] == "not_found" - assert result["error"] != "graduated" - - -@pytest.mark.asyncio -async def test_buy_auto_route_pumpswap_pool_not_found(tmp_keystore): - """Graduated but pool missing -> pumpswap_error.""" - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() - - result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", _VALID_MINT, 0.01) - - assert result["error"] == "pumpswap_error" - assert "No PumpSwap pool" in result["message"] - - -@pytest.mark.asyncio -async def test_sell_auto_route_pumpswap_pool_not_found(tmp_keystore): - """Graduated but pool missing -> pumpswap_error for sell.""" - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() - - result = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", _VALID_MINT, "all") - - assert result["error"] == "pumpswap_error" - assert "No PumpSwap pool" in result["message"] diff --git a/tests/test_core/test_info.py b/tests/test_core/test_info.py index f748bc1..42b32c3 100644 --- a/tests/test_core/test_info.py +++ b/tests/test_core/test_info.py @@ -1,272 +1,91 @@ -"""Unit tests for pumpfun_cli.core.info module.""" +"""Token info from the curve, and from the pool once it has graduated.""" -import asyncio -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest -from solders.pubkey import Pubkey -MINT_STR = "72xpy6cejkorDh8gx328CAW3Fq7uCQdCyXkSLAE5to5p" - - -def _make_state(*, complete: bool = False) -> dict: - """Return a realistic bonding curve state dict.""" - return { - "complete": complete, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - - -@pytest.fixture() -def _mock_rpc_not_found(): - """Mock RpcClient so get_account_info returns no value.""" - with patch("pumpfun_cli.core.info.RpcClient") as MockRpc: - instance = AsyncMock() - resp = MagicMock() - resp.value = None - instance.get_account_info.return_value = resp - MockRpc.return_value = instance - yield instance - - -@pytest.fixture() -def _mock_rpc_active(): - """Mock RpcClient + IDLParser for an active (not graduated) token.""" - state = _make_state(complete=False) - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 # dummy bytes - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - yield rpc_instance, idl_instance - - -@pytest.fixture() -def _mock_rpc_graduated(): - """Mock RpcClient + IDLParser for a graduated token with pool data.""" - state = _make_state(complete=True) - pool_info = { - "pool_address": "PoolAddr123", - "price_sol": 0.5, - "base_reserves": 1000.0, - "quote_reserves_sol": 500.0, - } - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - patch("pumpfun_cli.core.info.get_pumpswap_info", new_callable=AsyncMock) as mock_ps, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - mock_ps.return_value = pool_info - - yield rpc_instance, idl_instance, mock_ps - - -def test_get_token_info_invalid_mint(): - """Invalid mint address returns error dict without making any RPC calls.""" - from pumpfun_cli.core.info import get_token_info - - result = asyncio.run(get_token_info("http://localhost:8899", "invalidmintaddress")) - - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] - assert "hint" in result - - -def test_get_token_info_empty_mint(): - """Empty mint address returns error dict.""" - from pumpfun_cli.core.info import get_token_info - - result = asyncio.run(get_token_info("http://localhost:8899", "")) - - assert result["error"] == "invalid_address" - assert "hint" in result - - -def test_get_token_info_not_found(_mock_rpc_not_found): - """When RPC returns no account, result should have error='not_found'.""" - from pumpfun_cli.core.info import get_token_info - - result = asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - assert result["error"] == "not_found" - assert MINT_STR in result["message"] - - -def test_get_token_info_active(_mock_rpc_active): - """An active token on the bonding curve should return all expected fields.""" - from pumpfun_cli.core.info import get_token_info - - result = asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - expected_keys = { - "mint", - "bonding_curve", - "price_sol", - "bonding_progress", - "graduated", - "real_sol_reserves", - "virtual_sol_reserves", - "virtual_token_reserves", - "creator", - "is_mayhem_mode", - "is_cashback_coin", - } - assert expected_keys.issubset(result.keys()) - assert result["graduated"] is False - assert result["price_sol"] > 0 - assert "pool_address" not in result - assert result["mint"] == MINT_STR - - -def test_get_token_info_graduated_with_pool(_mock_rpc_graduated): - """A graduated token with pumpswap pool should include pool fields.""" - from pumpfun_cli.core.info import get_token_info - - result = asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - assert result["graduated"] is True - assert result["pool_address"] == "PoolAddr123" - assert result["base_reserves"] == 1000.0 - assert result["quote_reserves_sol"] == 500.0 - assert result["price_sol"] == 0.5 - - -def test_get_token_info_passes_rpc_timeout(): - """When called with rpc_timeout=45.0, RpcClient should be constructed with timeout=45.0.""" - from pumpfun_cli.core.info import get_token_info - - state = _make_state(complete=False) - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - asyncio.run(get_token_info("http://localhost:8899", MINT_STR, rpc_timeout=45.0)) - - MockRpc.assert_called_once_with("http://localhost:8899", timeout=45.0) - - -def test_get_token_info_graduated_pumpswap_timeout_surfaces_warning(): - """When get_pumpswap_info returns error, result has pumpswap_warning and pumpswap_error keys.""" - from pumpfun_cli.core.info import get_token_info - - state = _make_state(complete=True) - pumpswap_err = {"error": "pumpswap_error", "message": "Request timed out"} - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - patch("pumpfun_cli.core.info.get_pumpswap_info", new_callable=AsyncMock) as mock_ps, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - mock_ps.return_value = pumpswap_err - - result = asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - assert result["graduated"] is True - assert "pumpswap_warning" in result - assert "pumpswap_error" in result - assert result["price_sol"] > 0 # bonding curve price, not 0.0 - assert "pool_address" not in result - - -def test_get_token_info_graduated_pumpswap_no_pool_surfaces_warning(): - """'No PumpSwap pool found' error surfaces as warning.""" - from pumpfun_cli.core.info import get_token_info - - state = _make_state(complete=True) - pumpswap_err = {"error": "pumpswap_error", "message": "No PumpSwap pool found for mint"} - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - patch("pumpfun_cli.core.info.get_pumpswap_info", new_callable=AsyncMock) as mock_ps, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - mock_ps.return_value = pumpswap_err - - result = asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - assert result["graduated"] is True - assert "pumpswap_warning" in result - assert "No PumpSwap pool" in result["pumpswap_warning"] - assert "pool_address" not in result - - -def test_get_token_info_default_rpc_timeout_is_30(): - """Default RpcClient timeout should be 30.0 when no rpc_timeout passed.""" - from pumpfun_cli.core.info import get_token_info - - state = _make_state(complete=False) - with ( - patch("pumpfun_cli.core.info.RpcClient") as MockRpc, - patch("pumpfun_cli.core.info.IDLParser") as MockIDL, - ): - rpc_instance = AsyncMock() - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 64 - rpc_instance.get_account_info.return_value = resp - MockRpc.return_value = rpc_instance - - idl_instance = MagicMock() - idl_instance.decode_account_data.return_value = state - MockIDL.return_value = idl_instance - - asyncio.run(get_token_info("http://localhost:8899", MINT_STR)) - - MockRpc.assert_called_once_with("http://localhost:8899", timeout=30.0) +from pumpfun_cli.core.info import get_token_info +from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM +from tests.test_core.helpers import ( + COIN, + CREATOR, + T22_QUOTE, + add_pool, + build_curve_data, + chain_with_coin, +) + +MINT_STR = str(COIN) + + +def _patched(client): + return ( + patch("pumpfun_cli.core.info.RpcClient", return_value=client), + patch("pumpfun_cli.core.pumpswap.RpcClient", return_value=client), + ) + + +async def _info(client, mint=MINT_STR): + a, b = _patched(client) + with a, b: + return await get_token_info("http://rpc", mint) + + +async def test_invalid_and_missing(): + assert (await get_token_info("http://rpc", "bad"))["error"] == "invalid_address" + client = chain_with_coin() + client.accounts.clear() + assert (await _info(client))["error"] == "not_found" + + +async def test_active_sol_curve(): + client = chain_with_coin( + curve=build_curve_data(real_token=693_100_000_000_000, real_quote=4_000_000_000) + ) + info = await _info(client) + assert info["quote_symbol"] == "SOL" + assert info["price"] == pytest.approx(3e-8) + assert info["bonding_progress"] == pytest.approx(12.6, abs=0.1) + assert info["real_quote_reserves"] == 4.0 + assert info["creator"] == str(CREATOR) + assert info["graduated"] is False + + +async def test_custom_quote_curve_reports_its_asset_and_fee(): + client = chain_with_coin( + curve=build_curve_data( + quote_mint=T22_QUOTE, virtual_quote=5_000_000, creator_fee_bps=300, holder_reward=True + ), + quote_mint=T22_QUOTE, + quote_decimals=6, + quote_program=TOKEN_2022_PROGRAM, + ) + info = await _info(client) + assert info["quote_mint"] == str(T22_QUOTE) + assert info["price"] == pytest.approx(5.0 / 1e9) + assert info["creator_fee_bps"] == 300 + assert info["is_holder_reward"] is True + + +async def test_graduated_reads_the_pool(): + client = chain_with_coin(curve=build_curve_data(complete=True, virtual_quote=0)) + pool = add_pool(client, base_reserve=1_000_000_000, quote_reserve=3_000_000_000) + info = await _info(client) + assert info["graduated"] is True + assert info["bonding_progress"] == 100.0 + assert info["pool_address"] == str(pool["address"]) + assert info["price"] == pytest.approx(3.0 / 1_000) + + +async def test_graduated_without_pool_warns(): + client = chain_with_coin(curve=build_curve_data(complete=True)) + info = await _info(client) + assert info["pumpswap_error"] == "pumpswap_error" + assert "PumpSwap" in info["pumpswap_warning"] + + +async def test_timeout_is_forwarded(): + client = chain_with_coin() + with patch("pumpfun_cli.core.info.RpcClient", return_value=client) as cls: + await get_token_info("http://rpc", MINT_STR, rpc_timeout=12.5) + cls.assert_called_once_with("http://rpc", timeout=12.5) diff --git a/tests/test_core/test_launch.py b/tests/test_core/test_launch.py index edd09ac..edb2f92 100644 --- a/tests/test_core/test_launch.py +++ b/tests/test_core/test_launch.py @@ -1,91 +1,203 @@ -"""Tests for core/launch.py — cashback flag pass-through.""" +"""Launch: guards run before anything is uploaded or sent, then create (and buy).""" from unittest.mock import AsyncMock, patch import pytest - - -@pytest.mark.asyncio -async def test_launch_passes_cashback_false(tmp_keystore): - """launch_token passes is_cashback=False through to build_create_instructions.""" +from solders.pubkey import Pubkey + +from pumpfun_cli.core.launch import launch_token, opening_buy_tokens +from pumpfun_cli.protocol.address import find_ata, find_holder_rewards +from pumpfun_cli.protocol.contracts import ( + BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR, + CREATE_V2_DISCRIMINATOR, + EXTEND_ACCOUNT_DISCRIMINATOR, + PUMP_GLOBAL, + PUMP_PROGRAM, + TOKEN_2022_PROGRAM, + USDC_MINT, +) +from pumpfun_cli.protocol.quote import decode_global +from tests.test_core.helpers import ( + T22_QUOTE, + FakeClient, + add_quote_control, + build_global_data, +) + + +def _chain(**global_overrides): + client = FakeClient() + client.add(PUMP_GLOBAL, build_global_data(**global_overrides)) + add_quote_control(client, {T22_QUOTE: 7_000_000}) + client.add_mint(T22_QUOTE, 6, TOKEN_2022_PROGRAM) + return client + + +async def _launch(client, tmp_keystore, **kwargs): + upload = AsyncMock(return_value="https://ipfs.io/ipfs/x") + with ( + patch("pumpfun_cli.core.launch.RpcClient", return_value=client), + patch("pumpfun_cli.core.launch.upload_metadata", upload), + ): + result = await launch_token( + "http://rpc", tmp_keystore, "testpass", "Name", "TKR", "d", **kwargs + ) + return result, upload + + +def _pump(client): + return [i for i in client.sent[0]["instructions"] if i.program_id == PUMP_PROGRAM] + + +async def test_create_only(tmp_keystore): + client = _chain() + result, upload = await _launch(client, tmp_keystore) + assert result["action"] == "launch" + assert result["quote_symbol"] == "SOL" + upload.assert_awaited_once() + ixs = _pump(client) + assert [bytes(i.data[:8]) for i in ixs] == [ + CREATE_V2_DISCRIMINATOR, + EXTEND_ACCOUNT_DISCRIMINATOR, + ] + assert len(client.sent[0]["signers"]) == 2 + + +async def test_create_and_buy_in_one_transaction(tmp_keystore): + client = _chain() + result, _ = await _launch(client, tmp_keystore, initial_buy=0.5, slippage=10) + assert len(client.sent) == 1 + buy = _pump(client)[-1] + assert bytes(buy.data[:8]) == BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR + spend = int.from_bytes(bytes(buy.data[8:16]), "little") + floor = int.from_bytes(bytes(buy.data[16:24]), "little") + expected = opening_buy_tokens(decode_global(build_global_data()), spend, 30_000_000_000, None) + assert spend == 500_000_000 + assert floor == expected * 90 // 100 + assert result["expected_tokens"] == expected / 1e6 + + +async def test_holder_reward_buy_targets_the_holder_rewards_vault(tmp_keystore): + from pumpfun_cli.protocol.address import derive_creator_vault + + client = _chain() + await _launch(client, tmp_keystore, initial_buy=0.1, is_holder_reward=True) + create, _, buy = _pump(client) + mint = create.accounts[0].pubkey + assert bytes(create.data)[-1] == 1 + assert buy.accounts[16].pubkey == derive_creator_vault(find_holder_rewards(mint)) + + +async def test_custom_quote_with_creator_fee(tmp_keystore): + from pathlib import Path + + from pumpfun_cli.crypto import decrypt_keypair + + user = decrypt_keypair(Path(tmp_keystore), "testpass").pubkey() + client = _chain() + client.token_balances[find_ata(user, T22_QUOTE, TOKEN_2022_PROGRAM)] = 10_000_000 + result, _ = await _launch( + client, tmp_keystore, initial_buy=2, quote_mint_str=str(T22_QUOTE), creator_fee_bps=100 + ) + assert result["creator_fee_bps"] == 100 + create, _, buy = _pump(client) + assert create.accounts[16].pubkey == T22_QUOTE + assert int.from_bytes(bytes(buy.data[8:16]), "little") == 2_000_000 + + +@pytest.mark.parametrize( + "kwargs,message", + [ + ({"creator_fee_bps": 100}, "store 0"), + ({"is_mayhem": True, "quote_mint_str": str(USDC_MINT)}, "Mayhem"), + ({"quote_mint_str": str(Pubkey.new_unique())}, "admit"), + ({"quote_mint_str": str(T22_QUOTE), "creator_fee_bps": 900}, "maximum"), + ], +) +async def test_refused_before_upload(tmp_keystore, kwargs, message): + client = _chain() + client.add_mint(Pubkey.new_unique(), 6) + with pytest.raises(ValueError, match=message): + await _launch(client, tmp_keystore, **kwargs) + assert client.sent == [] + + +async def test_holder_reward_disabled(tmp_keystore): + client = _chain(is_holder_reward_enabled=False) + result, upload = await _launch(client, tmp_keystore, is_holder_reward=True) + assert result["error"] == "holder_reward_disabled" + upload.assert_not_awaited() + + +async def test_custom_quote_buy_needs_the_asset(tmp_keystore): + client = _chain() + result, upload = await _launch( + client, tmp_keystore, initial_buy=2, quote_mint_str=str(T22_QUOTE) + ) + assert result["error"] == "insufficient_balance" + upload.assert_not_awaited() + + +async def test_hosted_uri_skips_the_upload(tmp_keystore): + client = _chain() + result, upload = await _launch(client, tmp_keystore, uri="https://example.com/m.json") + upload.assert_not_awaited() + assert result["metadata_uri"] == "https://example.com/m.json" + create = _pump(client)[0] + assert b"https://example.com/m.json" in bytes(create.data) + + +async def test_refused_upload_points_at_uri(tmp_keystore): + client = _chain() with ( + patch("pumpfun_cli.core.launch.RpcClient", return_value=client), patch( "pumpfun_cli.core.launch.upload_metadata", - new_callable=AsyncMock, - return_value="https://ipfs.example.com/metadata.json", + AsyncMock(side_effect=RuntimeError("Metadata upload to pump.fun failed (HTTP 405)")), ), - patch("pumpfun_cli.core.launch.build_create_instructions", wraps=None) as mock_build, - patch("pumpfun_cli.core.launch.build_extend_account_instruction"), - patch( - "pumpfun_cli.core.launch.RpcClient", - ) as mock_rpc_cls, ): - mock_client = AsyncMock() - mock_client.send_tx = AsyncMock(return_value="fakesig123") - mock_rpc_cls.return_value = mock_client + result = await launch_token("http://rpc", tmp_keystore, "testpass", "N", "T", "d") + assert result["error"] == "ipfs_failed" + assert "--uri" in result["message"] + assert client.sent == [] - # build_create_instructions needs to return a list of instructions - from unittest.mock import MagicMock - mock_ix = MagicMock() - mock_build.return_value = [mock_ix] +def test_upload_identifies_itself(respx_mock): + import asyncio - from pumpfun_cli.core.launch import launch_token + import httpx - result = await launch_token( - rpc_url="https://fake.rpc", - keystore_path=tmp_keystore, - password="testpass", - name="TestToken", - ticker="TST", - description="A test token", - is_cashback=False, - ) + from pumpfun_cli.core.launch import upload_metadata + + route = respx_mock.post("https://pump.fun/api/ipfs").mock( + return_value=httpx.Response(200, json={"metadataUri": "https://ipfs.io/ipfs/x"}) + ) + assert asyncio.run(upload_metadata("N", "T", "d")) == "https://ipfs.io/ipfs/x" + assert route.calls.last.request.headers["user-agent"].startswith("pumpfun-cli") - mock_build.assert_called_once() - call_kwargs = mock_build.call_args - assert call_kwargs.kwargs.get("is_cashback") is False or ( - not call_kwargs.kwargs.get("is_cashback", True) - ) - assert result["is_cashback"] is False +def test_opening_buy_follows_the_curve(): + global_state = decode_global(build_global_data()) + small = opening_buy_tokens(global_state, 1_000_000, 30_000_000_000, None) + big = opening_buy_tokens(global_state, 5_000_000_000, 30_000_000_000, None) + reaching = 5_000_000_000 * (10_000 - 125) // 10_000 + linear_big = reaching * 1_073_000_000_000_000 // 30_000_000_000 + # Constant product: a 5 SOL buy into 30 SOL receives about 14% less than linear. + assert big < linear_big * 0.87 + assert small > 0 -@pytest.mark.asyncio -async def test_launch_passes_cashback_true(tmp_keystore): - """launch_token passes is_cashback=True through to build_create_instructions.""" + +async def test_upload_transport_error_points_at_uri(tmp_keystore): + import httpx + + client = _chain() with ( + patch("pumpfun_cli.core.launch.RpcClient", return_value=client), patch( "pumpfun_cli.core.launch.upload_metadata", - new_callable=AsyncMock, - return_value="https://ipfs.example.com/metadata.json", + AsyncMock(side_effect=httpx.ConnectTimeout("timed out")), ), - patch("pumpfun_cli.core.launch.build_create_instructions", wraps=None) as mock_build, - patch("pumpfun_cli.core.launch.build_extend_account_instruction"), - patch( - "pumpfun_cli.core.launch.RpcClient", - ) as mock_rpc_cls, ): - mock_client = AsyncMock() - mock_client.send_tx = AsyncMock(return_value="fakesig456") - mock_rpc_cls.return_value = mock_client - - from unittest.mock import MagicMock - - mock_ix = MagicMock() - mock_build.return_value = [mock_ix] - - from pumpfun_cli.core.launch import launch_token - - result = await launch_token( - rpc_url="https://fake.rpc", - keystore_path=tmp_keystore, - password="testpass", - name="TestToken", - ticker="TST", - description="A test token", - is_cashback=True, - ) - - mock_build.assert_called_once() - assert mock_build.call_args.kwargs["is_cashback"] is True - assert result["is_cashback"] is True + result = await launch_token("http://rpc", tmp_keystore, "testpass", "N", "T", "d") + assert result["error"] == "ipfs_failed" + assert "--uri" in result["message"] diff --git a/tests/test_core/test_pumpswap.py b/tests/test_core/test_pumpswap.py index f061633..ba1dc70 100644 --- a/tests/test_core/test_pumpswap.py +++ b/tests/test_core/test_pumpswap.py @@ -1,803 +1,261 @@ -"""Tests for core/pumpswap.py — mock RPC to test PumpSwap trade logic.""" +"""PumpSwap buys, sells and pool info, against an in-memory chain.""" -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest from pumpfun_cli.core.pumpswap import buy_pumpswap, get_pumpswap_info, sell_pumpswap +from pumpfun_cli.protocol.address import ( + derive_amm_pool_v2, + derive_amm_user_volume_accumulator, + find_ata, +) +from pumpfun_cli.protocol.client import TransactionFailedError from pumpfun_cli.protocol.contracts import ( - GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET, - STANDARD_PUMPSWAP_FEE_RECIPIENT, + PUMP_AMM_PROGRAM, + PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR, + PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR, + PUMPSWAP_SELL_DISCRIMINATOR, + TOKEN_2022_PROGRAM, TOKEN_PROGRAM, + USDC_MINT, +) +from tests.test_core.helpers import ( + BUYBACK_RECIPIENTS, + COIN, + PROTOCOL_RECIPIENTS, + RESERVED_RECIPIENT, + FakeClient, + add_pool, + build_curve_data, + chain_with_coin, ) -from tests.test_core.helpers import build_pool_data - - -def _mock_global_config_resp(): - """Build a mock GlobalConfig account response with standard fee recipient.""" - off = GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET - config_data = bytearray(off + 32) - config_data[off : off + 32] = bytes(STANDARD_PUMPSWAP_FEE_RECIPIENT) - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = bytes(config_data) - return resp - - -def _mock_pool_resp(pool_data): - """Build a mock get_account_info response for a pool account.""" - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = pool_data - return resp - -def _mock_pool_not_found(): - """Build a mock get_account_info response for pool not found.""" - resp = MagicMock() - resp.value = None - return resp +MINT_STR = str(COIN) -# --- invalid mint tests --- +def _patched(client): + return patch("pumpfun_cli.core.pumpswap.RpcClient", return_value=client) -@pytest.mark.asyncio -async def test_buy_pumpswap_invalid_mint(tmp_keystore): - """Invalid mint returns error dict without RPC calls.""" - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "invalidmintaddress", - 0.01, - ) - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] - - -@pytest.mark.asyncio -async def test_sell_pumpswap_invalid_mint(tmp_keystore): - """Invalid mint returns error dict without RPC calls.""" - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "invalidmintaddress", - "all", +def _swap(sent): + return next( + i + for i in sent["instructions"] + if i.program_id == PUMP_AMM_PROGRAM + and bytes(i.data[:8]) != PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR ) - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] -@pytest.mark.asyncio -async def test_get_pumpswap_info_invalid_mint(): - """Invalid mint returns error dict without RPC calls.""" - result = await get_pumpswap_info("http://rpc", "invalidmintaddress") - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] +def _user(tmp_keystore): + from pathlib import Path + from pumpfun_cli.crypto import decrypt_keypair -@pytest.mark.asyncio -async def test_buy_pumpswap_no_pool(tmp_keystore): - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # Pool lookup via get_account_info returns not found - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() + return decrypt_keypair(Path(tmp_keystore), "testpass").pubkey() - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - assert result["error"] == "pumpswap_error" - assert "No PumpSwap pool" in result["message"] +def _graduated_chain(**pool_kwargs): + client = chain_with_coin(curve=build_curve_data(complete=True, virtual_quote=0)) + pool = add_pool(client, **pool_kwargs) + return client, pool -def _mock_token_program_resp(): - """Build a mock account response for token program check.""" - resp = MagicMock() - resp.value = MagicMock() - resp.value.owner = TOKEN_PROGRAM - return resp - - -def _mock_vol_accumulator_resp(): - """Build a mock response for volume accumulator (not found).""" - resp = MagicMock() - resp.value = None - return resp - +@pytest.mark.parametrize("fn,arg", [(buy_pumpswap, 0.01), (sell_pumpswap, "all")]) +async def test_invalid_mint(tmp_keystore, fn, arg): + result = await fn("http://rpc", tmp_keystore, "testpass", "bad", arg) + assert result["error"] == "invalid_address" -@pytest.mark.asyncio -async def test_buy_pumpswap_no_liquidity(tmp_keystore): - pool_data = build_pool_data() - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # get_account_info calls: pool, token_program, GlobalConfig - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - # pool balances both 0 => price = 0 - client.get_token_account_balance.return_value = 0 - client.close = AsyncMock() +async def test_no_pool(tmp_keystore): + client = chain_with_coin(curve=build_curve_data(complete=True)) + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert result["error"] == "pumpswap_error" - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +async def test_no_liquidity(tmp_keystore): + client, _ = _graduated_chain(base_reserve=0) + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) assert result["error"] == "no_liquidity" -@pytest.mark.asyncio -async def test_buy_pumpswap_success(tmp_keystore): - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # get_account_info calls: pool, token_program, GlobalConfig, vol_accumulator - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - # base=1B tokens, quote=30B lamports => price = 30 - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "buysig" - client.close = AsyncMock() - +async def test_buy_sol_pool(tmp_keystore): + client, pool = _graduated_chain() + with _patched(client): result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, slippage=10 ) - - assert result["action"] == "buy" assert result["venue"] == "pumpswap" - assert result["signature"] == "buysig" - - -@pytest.mark.asyncio -async def test_sell_pumpswap_no_tokens(tmp_keystore): - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # get_account_info calls: pool, token_program, GlobalConfig - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.return_value = 0 - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", + assert result["quote_symbol"] == "SOL" + sent = client.sent[0] + swap = _swap(sent) + assert bytes(swap.data[:8]) == PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR + assert int.from_bytes(bytes(swap.data[8:16]), "little") == 10_000_000 + keys = [m.pubkey for m in swap.accounts] + assert keys[0] == pool["address"] + assert keys[9] in PROTOCOL_RECIPIENTS + assert keys[-2] in BUYBACK_RECIPIENTS + assert derive_amm_pool_v2(COIN) in keys + # The accumulator is missing on this chain, so it is initialized first. + first = sent["instructions"][0] + assert bytes(first.data) == PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR + # Unspent SOL comes back: the throwaway WSOL account is closed last. + assert sent["instructions"][-1].program_id == TOKEN_PROGRAM + + +async def test_buy_skips_accumulator_init_when_present(tmp_keystore): + client, _ = _graduated_chain() + client.add(derive_amm_user_volume_accumulator(_user(tmp_keystore)), b"\x00" * 8) + with _patched(client): + await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + first = client.sent[0]["instructions"][0] + assert bytes(first.data[:8]) != PUMPSWAP_INIT_USER_VOLUME_ACCUMULATOR_DISCRIMINATOR + + +async def test_mayhem_pool_pays_reserved_recipient(tmp_keystore): + client, _ = _graduated_chain(mayhem=True) + with _patched(client): + await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert _swap(client.sent[0]).accounts[9].pubkey == RESERVED_RECIPIENT + + +async def test_virtual_reserves_raise_the_quote(tmp_keystore): + plain, _ = _graduated_chain() + boosted, _ = _graduated_chain(virtual_quote=80_000_000_000) + with _patched(plain): + a = await buy_pumpswap( + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, dry_run=True ) - - assert result["error"] == "no_tokens" - - -@pytest.mark.asyncio -async def test_sell_pumpswap_success(tmp_keystore): - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # get_account_info calls: pool, token_program, GlobalConfig - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - # first call: user token balance, then pool base, pool quote for price - client.get_token_account_balance.side_effect = [1_000_000, 1_000_000_000, 30_000_000_000] - client.send_tx.return_value = "sellsig" - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", + with _patched(boosted): + b = await buy_pumpswap( + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, dry_run=True ) - - assert result["action"] == "sell" - assert result["venue"] == "pumpswap" - assert result["signature"] == "sellsig" - - -@pytest.mark.asyncio -async def test_get_pumpswap_info_success(): - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - # get_account_info calls: pool - client.get_account_info.return_value = _mock_pool_resp(pool_data) - # get_pool_balances called once, used for both price and reserves - client.get_token_account_balance.side_effect = [ - 1_000_000_000, - 30_000_000_000, - ] - client.close = AsyncMock() - - result = await get_pumpswap_info( - "http://rpc", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ) - - assert "pool_address" in result - assert result["price_sol"] == 30.0 - assert result["mayhem_mode"] is False - - -@pytest.mark.asyncio -async def test_get_pumpswap_info_no_pool(): - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() - - result = await get_pumpswap_info( - "http://rpc", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ) - - assert result["error"] == "pumpswap_error" - - -@pytest.mark.asyncio -async def test_buy_pumpswap_custom_priority_fee(tmp_keystore): - """Custom priority_fee and compute_units are forwarded to send_tx.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "buysig" - client.close = AsyncMock() - - await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - priority_fee=55_000, - compute_units=350_000, - ) - - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("priority_fee") == 55_000 - assert call_kwargs.kwargs.get("compute_units") == 350_000 - - -@pytest.mark.asyncio -async def test_sell_pumpswap_custom_priority_fee(tmp_keystore): - """Custom priority_fee and compute_units are forwarded to send_tx for sell.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000, 1_000_000_000, 30_000_000_000] - client.send_tx.return_value = "sellsig" - client.close = AsyncMock() - - await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - priority_fee=88_000, - compute_units=250_000, - ) - - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("priority_fee") == 88_000 - assert call_kwargs.kwargs.get("compute_units") == 250_000 - - -@pytest.mark.asyncio -async def test_buy_pumpswap_dry_run(tmp_keystore): - """dry_run=True returns simulation dict without calling send_tx.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - dry_run=True, - ) - - assert result["dry_run"] is True - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" - assert result["sol_in"] == 0.01 - assert result["expected_tokens"] > 0 - assert result["effective_price_sol"] > 0 - assert result["spot_price_sol"] > 0 - assert "price_impact_pct" in result - assert "signature" not in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_sell_pumpswap_dry_run(tmp_keystore): - """dry_run=True returns simulation dict without calling send_tx.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000, 1_000_000_000, 30_000_000_000] - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - dry_run=True, - ) - - assert result["dry_run"] is True - assert result["action"] == "sell" - assert result["venue"] == "pumpswap" - assert result["tokens_in"] > 0 - assert result["expected_sol"] > 0 - assert "price_impact_pct" in result - assert "signature" not in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_buy_pumpswap_slippage_zero(tmp_keystore): - """Buy with slippage=0 means min_base_amount_out == estimated_tokens.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - # base=1B tokens, quote=30B lamports - base_balance = 1_000_000_000 - quote_balance = 30_000_000_000 - client.get_token_account_balance.side_effect = [base_balance, quote_balance] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "buysig_slippage0" - client.close = AsyncMock() - - sol_amount = 0.01 - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - sol_amount, - slippage=0, - ) - - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" - assert result["signature"] == "buysig_slippage0" - - # Verify the formula: with slippage=0, min_base_amount_out == estimated_tokens - sol_lamports = int(sol_amount * 1_000_000_000) - effective_sol = sol_lamports * 99 // 100 - estimated_tokens = base_balance * effective_sol // (quote_balance + effective_sol) - min_base_amount_out = estimated_tokens * (100 - 0) // 100 - assert min_base_amount_out == estimated_tokens - assert result["tokens_received"] == estimated_tokens / (10**6) - - -@pytest.mark.asyncio -async def test_sell_pumpswap_partial_amount(tmp_keystore): - """Sell a specific token amount (not 'all') — should NOT query user balance.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - # Balance check + pool balances: user_balance, pool_base, pool_quote - user_balance = 500_000_000_000 # 500 tokens — enough - base_balance = 1_000_000_000 - quote_balance = 30_000_000_000 - client.get_token_account_balance.side_effect = [user_balance, base_balance, quote_balance] - client.send_tx.return_value = "sellsig_partial" - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "500", - ) - - assert result["action"] == "sell" - assert result["venue"] == "pumpswap" - assert result["signature"] == "sellsig_partial" - assert result["tokens_sold"] == 500.0 - # get_token_account_balance called 3 times: balance check + pool base + pool quote - assert client.get_token_account_balance.call_count == 3 - - -# --- pre-trade balance validation tests --- - - -@pytest.mark.asyncio -async def test_buy_pumpswap_insufficient_sol(tmp_keystore): - """Buy with wallet balance too low returns insufficient_balance.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - # base=1B tokens, quote=30B lamports => has liquidity - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 1_000 # very low - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - + assert b["spot_price"] == pytest.approx(2 * a["spot_price"]) + assert b["expected_tokens"] < a["expected_tokens"] + + +async def test_usdc_coin_routes_to_its_usdc_pool(tmp_keystore): + user = _user(tmp_keystore) + client = chain_with_coin(curve=build_curve_data(complete=True, quote_mint=USDC_MINT)) + pool = add_pool(client, quote_mint=USDC_MINT, quote_reserve=50_000_000_000) + client.token_balances[find_ata(user, USDC_MINT, TOKEN_PROGRAM)] = 10_000_000 + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 5) + assert result["quote_symbol"] == "USDC" + swap = _swap(client.sent[0]) + assert swap.accounts[0].pubkey == pool["address"] + assert int.from_bytes(bytes(swap.data[8:16]), "little") == 5_000_000 + assert swap.accounts[6].pubkey == find_ata(user, USDC_MINT, TOKEN_PROGRAM) + + +async def test_usdc_buy_needs_usdc(tmp_keystore): + client = chain_with_coin(curve=build_curve_data(complete=True, quote_mint=USDC_MINT)) + add_pool(client, quote_mint=USDC_MINT) + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 5) assert result["error"] == "insufficient_balance" - assert "available_sol" in result - assert "required_sol" in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_sell_pumpswap_insufficient_tokens(tmp_keystore): - """Sell specific amount exceeding token balance returns insufficient_balance.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - # User token balance = 500_000 (0.5 tokens), then pool balances - client.get_token_account_balance.side_effect = [ - 500_000, - 1_000_000_000, - 30_000_000_000, - ] - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1000", # asking to sell 1000 but only have 0.5 - ) - - assert result["error"] == "insufficient_balance" - assert "available_tokens" in result - assert "required_tokens" in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_buy_pumpswap_sufficient_sol_proceeds(tmp_keystore): - """Buy with adequate balance proceeds normally (regression guard).""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 # 10 SOL — plenty - client.send_tx.return_value = "buysig_sufficient" - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" - assert result["signature"] == "buysig_sufficient" - client.send_tx.assert_called_once() - - -@pytest.mark.asyncio -async def test_buy_pumpswap_dry_run_insufficient_sol_includes_warning(tmp_keystore): - """Dry-run with low balance still returns simulation but includes balance_warning.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 1_000 # very low - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - dry_run=True, - ) - assert result["dry_run"] is True - assert "balance_warning" in result - client.send_tx.assert_not_called() - -# --- TransactionFailedError / slippage tests --- - - -@pytest.mark.asyncio -async def test_buy_pumpswap_slippage_error(tmp_keystore): - """PumpSwap slippage error code 6040 returns slippage error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6040))))" - ) - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - - assert result["error"] == "slippage" - assert result["error_code"] == 6040 - - -@pytest.mark.asyncio -async def test_sell_pumpswap_slippage_error(tmp_keystore): - """PumpSwap slippage error code 6004 returns slippage error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000, 1_000_000_000, 30_000_000_000] - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6004))))" - ) - client.close = AsyncMock() - - result = await sell_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - ) - - assert result["error"] == "slippage" - assert result["error_code"] == 6004 - - -@pytest.mark.asyncio -async def test_buy_pumpswap_non_slippage_tx_error(tmp_keystore): - """Non-slippage error code returns tx_error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(9999))))" - ) - client.close = AsyncMock() - - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, +async def test_insufficient_sol(tmp_keystore): + client, _ = _graduated_chain() + client.default_balance = 1_000 + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + dry = await buy_pumpswap( + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, dry_run=True ) + assert result["error"] == "insufficient_balance" + assert "balance_warning" in dry + assert client.sent == [] + + +async def test_sell_all_non_canonical_decimals(tmp_keystore): + """A non-canonical pool may carry a 9-decimal base token; amounts follow the mint.""" + user = _user(tmp_keystore) + client = FakeClient() + client.add_mint(COIN, 9, TOKEN_PROGRAM) + add_pool(client, coin_creator=None) + client.token_balances[find_ata(user, COIN, TOKEN_PROGRAM)] = 3_000_000_000 + with _patched(client): + result = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + assert result["tokens_sold"] == 3.0 + swap = _swap(client.sent[0]) + assert bytes(swap.data[:8]) == PUMPSWAP_SELL_DISCRIMINATOR + assert derive_amm_pool_v2(COIN) not in [m.pubkey for m in swap.accounts] + + +async def test_sell_partial_and_insufficient(tmp_keystore): + user = _user(tmp_keystore) + client, _ = _graduated_chain() + client.token_balances[find_ata(user, COIN, TOKEN_2022_PROGRAM)] = 2_000_000 + with _patched(client): + ok = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, "1.5") + too_much = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, "3") + assert int.from_bytes(bytes(_swap(client.sent[0]).data[8:16]), "little") == 1_500_000 + assert ok["tokens_sold"] == 1.5 + assert too_much["error"] == "insufficient_balance" + + +async def test_sell_nothing_held(tmp_keystore): + client, _ = _graduated_chain() + with _patched(client): + result = await sell_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + assert result["error"] == "no_tokens" - assert result["error"] == "tx_error" - assert result["error_code"] == 9999 +@pytest.mark.parametrize( + "code,error", [(6004, "slippage"), (6040, "slippage"), (6053, "tx_error")] +) +async def test_onchain_errors(tmp_keystore, code, error): + client, _ = _graduated_chain() + client.send_error = TransactionFailedError({"InstructionError": [4, {"Custom": code}]}) + with _patched(client): + result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert result["error"] == error -@pytest.mark.asyncio -async def test_buy_pumpswap_pool_not_found_still_works(tmp_keystore): - """Pool-not-found RuntimeError still returns pumpswap_error (regression).""" - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() - result = await buy_pumpswap( +async def test_custom_budget_is_forwarded(tmp_keystore): + client, _ = _graduated_chain() + with _patched(client): + await buy_pumpswap( "http://rpc", tmp_keystore, "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + MINT_STR, 0.01, + priority_fee=777, + compute_units=123_456, ) + assert (client.sent[0]["priority_fee"], client.sent[0]["compute_units"]) == (777, 123_456) - assert result["error"] == "pumpswap_error" - assert "No PumpSwap pool" in result["message"] - - -@pytest.mark.asyncio -async def test_buy_pumpswap_without_confirm(tmp_keystore): - """Buy without confirm=True — result should NOT contain 'confirmed' key.""" - pool_data = build_pool_data() - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [ - _mock_pool_resp(pool_data), - _mock_token_program_resp(), - _mock_global_config_resp(), - _mock_vol_accumulator_resp(), - ] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "buysig_noconfirm" - client.close = AsyncMock() - result = await buy_pumpswap( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - confirm=False, - ) +async def test_info(): + client, pool = _graduated_chain(base_reserve=1_000_000_000, quote_reserve=2_000_000_000) + with _patched(client): + info = await get_pumpswap_info("http://rpc", MINT_STR) + assert info["pool_address"] == str(pool["address"]) + assert info["quote_symbol"] == "SOL" + assert info["price"] == pytest.approx(2.0 / 1_000) + assert info["base_reserves"] == 1_000.0 + assert info["quote_reserves"] == 2.0 - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" - assert result["signature"] == "buysig_noconfirm" - assert "confirmed" not in result +async def test_info_without_pool(): + client = chain_with_coin() + with _patched(client): + info = await get_pumpswap_info("http://rpc", MINT_STR) + assert info["error"] == "pumpswap_error" -@pytest.mark.asyncio -async def test_get_pumpswap_info_uses_30s_default_timeout(): - """get_pumpswap_info should construct RpcClient with timeout=30.0 by default.""" - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.return_value = _mock_pool_not_found() - client.close = AsyncMock() - await get_pumpswap_info("http://rpc", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") +async def test_sol_buy_wraps_ten_percent_headroom_exactly(tmp_keystore): + client, _ = _graduated_chain() + with _patched(client): + await buy_pumpswap("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.29) + from solders.system_program import ID as SYSTEM_PROGRAM_ID + from solders.system_program import decode_create_account_with_seed - MockClient.assert_called_once_with("http://rpc", timeout=30.0) + create = next(i for i in client.sent[0]["instructions"] if i.program_id == SYSTEM_PROGRAM_ID) + lamports = decode_create_account_with_seed(create)["lamports"] + assert lamports - 2_039_280 == 319_000_000 # 0.29 SOL + 10%, rent on top diff --git a/tests/test_core/test_token_types.py b/tests/test_core/test_token_types.py index 0ada617..bd6e961 100644 --- a/tests/test_core/test_token_types.py +++ b/tests/test_core/test_token_types.py @@ -1,283 +1,54 @@ -"""Core trade tests for all token type combinations. +"""Every kind of coin the program issues trades through the same v2 path. -Tests buy_token and sell_token with different bonding curve state flags: -mayhem, cashback, SPL vs Token-2022. Verifies correct instruction -building and fee routing through mocked RPC. - -Ref: docs/token-types-test-matrix.md +Mayhem, cashback, holder-reward and legacy SPL Token coins differ only in +which fee recipients and token program the accounts derive from. """ -from unittest.mock import AsyncMock, MagicMock, patch +from pathlib import Path +from unittest.mock import patch import pytest -from solders.pubkey import Pubkey from pumpfun_cli.core.trade import buy_token, sell_token +from pumpfun_cli.crypto import decrypt_keypair +from pumpfun_cli.protocol.address import find_ata from pumpfun_cli.protocol.contracts import ( + NORMAL_FEE_RECIPIENTS, + PUMP_PROGRAM, + RESERVED_FEE_RECIPIENTS, TOKEN_2022_PROGRAM, TOKEN_PROGRAM, ) - -_MINT_STR = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" -_CREATOR_BYTES = bytes(Pubkey.from_string("11111111111111111111111111111112")) - - -def _bc_state( - *, - is_mayhem: bool = False, - is_cashback: bool = False, - complete: bool = False, -): - """Build a bonding curve state dict with given flags.""" - return { - "complete": complete, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": _CREATOR_BYTES, - "is_mayhem_mode": is_mayhem, - "is_cashback_coin": is_cashback, - } - - -def _patch_token_program(program: Pubkey): - return patch( - "pumpfun_cli.core.trade.get_token_program_id", - new=AsyncMock(return_value=program), - ) - - -def _setup_mocks(MockClient, MockIDL, state, *, side: str = "buy"): - """Set up mocked RPC client and IDL for buy or sell tests.""" - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.send_tx.return_value = "sig" - client.close = AsyncMock() - - if side == "buy": - client.get_balance.return_value = 10_000_000_000 - else: - client.get_token_account_balance.return_value = 1_000_000 - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = state - idl.get_instruction_discriminators.return_value = {side: b"\x00" * 8} - - return client, idl - - -@pytest.mark.asyncio -async def test_buy_standard_token2022(tmp_keystore): - """BC-BUY-1: Standard Token-2022 buy succeeds.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state()) - - result = await buy_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, 0.01) - - assert result["action"] == "buy" - assert result["signature"] == "sig" - - -@pytest.mark.asyncio -async def test_buy_mayhem_token(tmp_keystore): - """BC-BUY-2: Mayhem token buy — passes is_mayhem=True to instruction builder.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_buy_exact_sol_in_instructions") as mock_build, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(is_mayhem=True)) - mock_build.return_value = [] - - await buy_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, 0.01) - - assert mock_build.call_args.kwargs.get("is_mayhem") is True - - -@pytest.mark.asyncio -async def test_buy_legacy_spl_token(tmp_keystore): - """BC-BUY-5: Legacy SPL Token buy — token_program=TOKEN_PROGRAM passed through.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_buy_exact_sol_in_instructions") as mock_build, - _patch_token_program(TOKEN_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state()) - mock_build.return_value = [] - - await buy_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, 0.01) - - assert mock_build.call_args.kwargs.get("token_program") == TOKEN_PROGRAM - - -@pytest.mark.asyncio -async def test_sell_standard_no_cashback(tmp_keystore): - """BC-SELL-1: Standard sell — is_cashback=False passed to builder.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(is_cashback=False), side="sell") - mock_build.return_value = [] - - await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "1.0") - - assert mock_build.call_args.kwargs.get("is_cashback") is False - - -@pytest.mark.asyncio -async def test_sell_mayhem_token(tmp_keystore): - """BC-SELL-2: Mayhem sell — is_mayhem=True passed to builder.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(is_mayhem=True), side="sell") - mock_build.return_value = [] - - await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "1.0") - - assert mock_build.call_args.kwargs.get("is_mayhem") is True - - -@pytest.mark.asyncio -async def test_sell_cashback_token(tmp_keystore): - """BC-SELL-3: Cashback sell — is_cashback=True passed to builder.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(is_cashback=True), side="sell") - mock_build.return_value = [] - - await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "1.0") - - assert mock_build.call_args.kwargs.get("is_cashback") is True - - -@pytest.mark.asyncio -async def test_sell_mayhem_cashback_token(tmp_keystore): - """BC-SELL-4: Mayhem + cashback sell — both flags passed through.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(is_mayhem=True, is_cashback=True), side="sell") - mock_build.return_value = [] - - await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "1.0") - - kwargs = mock_build.call_args.kwargs - assert kwargs.get("is_mayhem") is True - assert kwargs.get("is_cashback") is True - - -@pytest.mark.asyncio -async def test_sell_legacy_spl_token(tmp_keystore): - """BC-SELL-5: Legacy SPL Token sell — token_program=TOKEN_PROGRAM passed through.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build, - _patch_token_program(TOKEN_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(), side="sell") - mock_build.return_value = [] - - await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "1.0") - - assert mock_build.call_args.kwargs.get("token_program") == TOKEN_PROGRAM - - -@pytest.mark.asyncio -async def test_buy_graduated_token_error(tmp_keystore): - """BC-BUY-8: Buying a graduated token returns error.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(complete=True)) - - result = await buy_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, 0.01) - - assert result["error"] == "graduated" - - -@pytest.mark.asyncio -async def test_sell_graduated_token_error(tmp_keystore): - """BC-SELL-10: Selling a graduated token returns error.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _patch_token_program(TOKEN_2022_PROGRAM), - ): - _setup_mocks(MockClient, MockIDL, _bc_state(complete=True), side="sell") - - result = await sell_token("http://rpc", tmp_keystore, "testpass", _MINT_STR, "all") - - assert result["error"] == "graduated" - - -@pytest.mark.asyncio -async def test_pumpswap_buy_passes_token_program(tmp_keystore): - """PS-BUY-1/PS-BUY-2: PumpSwap buy forwards detected token_program.""" - from pumpfun_cli.core.pumpswap import buy_pumpswap - from pumpfun_cli.protocol.contracts import ( - GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET, - STANDARD_PUMPSWAP_FEE_RECIPIENT, - ) - from tests.test_core.helpers import build_pool_data - - pool_data = build_pool_data() - - pool_resp = MagicMock() - pool_resp.value = MagicMock() - pool_resp.value.data = pool_data - - off = GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET - config_data = bytearray(off + 32) - config_data[off : off + 32] = bytes(STANDARD_PUMPSWAP_FEE_RECIPIENT) - gc_resp = MagicMock() - gc_resp.value = MagicMock() - gc_resp.value.data = bytes(config_data) - - tp_resp = MagicMock() - tp_resp.value = MagicMock() - tp_resp.value.owner = TOKEN_2022_PROGRAM - - vol_resp = MagicMock() - vol_resp.value = None - - with patch("pumpfun_cli.core.pumpswap.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_account_info.side_effect = [pool_resp, tp_resp, gc_resp, vol_resp] - client.get_token_account_balance.side_effect = [1_000_000_000, 30_000_000_000] - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "ps_sig" - client.close = AsyncMock() - - result = await buy_pumpswap("http://rpc", tmp_keystore, "testpass", _MINT_STR, 0.01) - - assert result["action"] == "buy" - assert result["venue"] == "pumpswap" +from tests.test_core.helpers import COIN, build_curve_data, chain_with_coin + +CASES = { + "standard": ({}, TOKEN_2022_PROGRAM, NORMAL_FEE_RECIPIENTS), + "mayhem": ({"mayhem": True}, TOKEN_2022_PROGRAM, RESERVED_FEE_RECIPIENTS), + "cashback": ({"cashback": True}, TOKEN_2022_PROGRAM, NORMAL_FEE_RECIPIENTS), + "holder_reward": ({"holder_reward": True}, TOKEN_2022_PROGRAM, NORMAL_FEE_RECIPIENTS), + "legacy_spl": ({"length": 151}, TOKEN_PROGRAM, NORMAL_FEE_RECIPIENTS), +} + + +def _chain(curve_kwargs, program): + client = chain_with_coin(curve=build_curve_data(**curve_kwargs)) + client.add_mint(COIN, 6, program) + return client + + +@pytest.mark.parametrize("case", CASES) +async def test_buy_and_sell(tmp_keystore, case): + curve_kwargs, program, recipients = CASES[case] + user = decrypt_keypair(Path(tmp_keystore), "testpass").pubkey() + client = _chain(curve_kwargs, program) + client.token_balances[find_ata(user, COIN, program)] = 1_000_000 + with patch("pumpfun_cli.core.trade.RpcClient", return_value=client): + bought = await buy_token("http://rpc", tmp_keystore, "testpass", str(COIN), 0.01) + sold = await sell_token("http://rpc", tmp_keystore, "testpass", str(COIN), "all") + assert bought["action"] == "buy" and sold["action"] == "sell" + for sent in client.sent: + ix = next(i for i in sent["instructions"] if i.program_id == PUMP_PROGRAM) + keys = [m.pubkey for m in ix.accounts] + assert keys[3] == program + assert keys[6] in recipients + assert keys[14] == find_ata(user, COIN, program) diff --git a/tests/test_core/test_trade.py b/tests/test_core/test_trade.py index ba3e699..5f2bd4e 100644 --- a/tests/test_core/test_trade.py +++ b/tests/test_core/test_trade.py @@ -1,17 +1,10 @@ -"""Tests for core/trade.py — mock RPC to test buy/sell/extras logic.""" +"""Bonding-curve trades, payouts and migration, against an in-memory chain.""" -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest from solders.pubkey import Pubkey -from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM - -_PATCH_TOKEN_PROG = patch( - "pumpfun_cli.core.trade.get_token_program_id", - new=AsyncMock(return_value=TOKEN_2022_PROGRAM), -) - from pumpfun_cli.core.trade import ( buy_token, claim_cashback, @@ -20,1343 +13,347 @@ migrate_token, sell_token, ) +from pumpfun_cli.protocol.address import ( + derive_amm_creator_vault, + derive_amm_pool, + derive_creator_vault, + find_ata, + find_user_volume_accumulator, +) +from pumpfun_cli.protocol.client import TransactionFailedError +from pumpfun_cli.protocol.contracts import ( + ASSOCIATED_TOKEN_PROGRAM, + BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR, + CLAIM_CASHBACK_V2_DISCRIMINATOR, + COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR, + COLLECT_CREATOR_FEE_V2_DISCRIMINATOR, + MIGRATE_V2_DISCRIMINATOR, + PUMP_BUY_COMPUTE_UNITS, + PUMP_NON_SOL_TRADE_COMPUTE_UNITS, + PUMP_PROGRAM, + SELL_V2_DISCRIMINATOR, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + WSOL_MINT, +) +from tests.test_core.helpers import ( + COIN, + CREATOR, + GLOBAL_DEFAULTS, + T22_QUOTE, + FakeClient, + build_curve_data, + chain_with_coin, +) -# --- invalid mint tests --- - - -@pytest.mark.asyncio -async def test_buy_token_invalid_mint(tmp_keystore): - """Invalid mint returns error dict without RPC calls.""" - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "invalidmintaddress", - 0.01, - ) - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] - - -@pytest.mark.asyncio -async def test_sell_token_invalid_mint(tmp_keystore): - """Invalid mint returns error dict without RPC calls.""" - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "invalidmintaddress", - "all", - ) - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] - +MINT_STR = str(COIN) -@pytest.mark.asyncio -async def test_migrate_token_invalid_mint(tmp_keystore): - """Invalid mint returns error dict without RPC calls.""" - result = await migrate_token( - "http://rpc", - tmp_keystore, - "testpass", - "invalidmintaddress", - ) - assert result["error"] == "invalid_address" - assert "invalidmintaddress" in result["message"] +def _patched(client: FakeClient): + return patch("pumpfun_cli.core.trade.RpcClient", return_value=client) -# --- buy_token tests --- +def _pump_ix(sent: dict): + return next(i for i in sent["instructions"] if i.program_id == PUMP_PROGRAM) -@pytest.mark.asyncio -async def test_buy_token_not_found(tmp_keystore): - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient, _PATCH_TOKEN_PROG: - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = None - client.get_account_info.return_value = resp - client.close = AsyncMock() - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +def _user_from(tmp_keystore): + from pathlib import Path - assert result["error"] == "not_found" + from pumpfun_cli.crypto import decrypt_keypair + return decrypt_keypair(Path(tmp_keystore), "testpass").pubkey() -@pytest.mark.asyncio -async def test_buy_token_graduated(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = {"complete": True} - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +# --- input validation --- - assert result["error"] == "graduated" - -@pytest.mark.asyncio -async def test_buy_token_success(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "fakesig123" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - - assert result["action"] == "buy" - assert result["signature"] == "fakesig123" - assert result["sol_spent"] == 0.01 - assert "explorer" in result - - -# --- sell_token tests --- +@pytest.mark.parametrize( + "fn,args", [(buy_token, (0.01,)), (sell_token, ("all",)), (migrate_token, ())] +) +async def test_invalid_mint_is_refused_before_any_rpc(tmp_keystore, fn, args): + result = await fn("http://rpc", tmp_keystore, "testpass", "not-a-mint", *args) + assert result["error"] == "invalid_address" -@pytest.mark.asyncio -async def test_sell_token_not_found(tmp_keystore): - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient, _PATCH_TOKEN_PROG: - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = None - client.get_account_info.return_value = resp - client.close = AsyncMock() +# --- buy --- - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - ) +async def test_buy_no_curve(tmp_keystore): + client = FakeClient() + client.add_mint(COIN, 6) + with _patched(client): + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) assert result["error"] == "not_found" -@pytest.mark.asyncio -async def test_sell_token_graduated(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = {"complete": True} - - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - ) - +async def test_buy_graduated_curve_defers_to_pumpswap(tmp_keystore): + client = chain_with_coin(curve=build_curve_data(complete=True)) + with _patched(client): + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) assert result["error"] == "graduated" -@pytest.mark.asyncio -async def test_sell_token_no_tokens(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 0 - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - } - - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "all", - ) - - assert result["error"] == "no_tokens" - - -@pytest.mark.asyncio -async def test_sell_token_success(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.send_tx.return_value = "sellsig456" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - ) - - assert result["action"] == "sell" - assert result["signature"] == "sellsig456" - - -# --- claim_cashback tests --- - - -@pytest.mark.asyncio -async def test_claim_cashback_success(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - ): - client = AsyncMock() - MockClient.return_value = client - client.send_tx.return_value = "cashbacksig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.get_instruction_discriminators.return_value = {"claim_cashback": b"\x00" * 8} - - result = await claim_cashback("http://rpc", tmp_keystore, "testpass") - - assert result["action"] == "claim_cashback" - assert result["signature"] == "cashbacksig" - - -# --- close_volume_accumulator tests --- - - -@pytest.mark.asyncio -async def test_close_volume_accumulator_success(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - ): - client = AsyncMock() - MockClient.return_value = client - client.send_tx.return_value = "closesig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.get_instruction_discriminators.return_value = { - "close_user_volume_accumulator": b"\x00" * 8 - } - - result = await close_volume_accumulator("http://rpc", tmp_keystore, "testpass") - - assert result["action"] == "close_volume_accumulator" - assert result["signature"] == "closesig" - - -# --- migrate_token tests --- - - -@pytest.mark.asyncio -async def test_migrate_not_found(tmp_keystore): - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = None - client.get_account_info.return_value = resp - client.close = AsyncMock() - - result = await migrate_token( - "http://rpc", tmp_keystore, "testpass", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ) - - assert result["error"] == "not_found" - - -@pytest.mark.asyncio -async def test_migrate_not_complete(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = {"complete": False} - - result = await migrate_token( - "http://rpc", tmp_keystore, "testpass", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ) - - assert result["error"] == "not_complete" - - -@pytest.mark.asyncio -async def test_migrate_success(tmp_keystore): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - ): - client = AsyncMock() - MockClient.return_value = client - - # First call: bonding curve, second call: global account - bc_resp = MagicMock() - bc_resp.value = MagicMock() - bc_resp.value.data = b"\x00" * 200 - global_resp = MagicMock() - global_resp.value = MagicMock() - global_resp.value.data = b"\x00" * 200 - client.get_account_info.side_effect = [bc_resp, global_resp] - client.send_tx.return_value = "migratesig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - # First call decodes BondingCurve, second decodes Global - idl.decode_account_data.side_effect = [ - {"complete": True}, - {"withdraw_authority": bytes(Pubkey.from_string("11111111111111111111111111111112"))}, - ] - idl.get_instruction_discriminators.return_value = {"migrate": b"\x00" * 8} - - result = await migrate_token( - "http://rpc", tmp_keystore, "testpass", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ) - - assert result["action"] == "migrate" - assert result["signature"] == "migratesig" - - -# --- collect_creator_fees tests --- - - -@pytest.mark.asyncio -async def test_collect_creator_fees_no_fees(tmp_keystore): - """When both vaults are empty, returns no_fees error.""" - with patch("pumpfun_cli.core.trade.RpcClient") as MockClient: - client = AsyncMock() - MockClient.return_value = client - client.get_balance.return_value = 0 - client.get_token_account_balance.return_value = 0 - client.close = AsyncMock() - - result = await collect_creator_fees( - rpc_url="http://fake", - keystore_path=str(tmp_keystore), - password="testpass", - ) - assert result["error"] == "no_fees" - - -# --- slippage and confirm tests --- - - -@pytest.mark.asyncio -async def test_buy_token_slippage_zero(tmp_keystore): - """Buy with slippage=0 means min_tokens_out == tokens_out (no tolerance).""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "slippage0sig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - +async def test_buy_sol_coin_spends_exact_quote(tmp_keystore): + client = chain_with_coin() + with _patched(client): result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - slippage=0, + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, slippage=10 ) - assert result["action"] == "buy" - assert result["signature"] == "slippage0sig" - # With slippage=0, min_tokens_out = tokens_out * 100/100 = tokens_out exactly - # Verify send_tx was actually called (transaction was submitted) - client.send_tx.assert_called_once() - - -@pytest.mark.asyncio -async def test_sell_token_slippage_propagation(tmp_keystore): - """Sell with slippage=0 vs slippage=50 should produce different min_sol values - passed to build_sell_instructions.""" - captured_min_sols = [] - - for slippage_val in (0, 50): - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build_sell, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.send_tx.return_value = "sellsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - mock_build_sell.return_value = [] - - await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - slippage=slippage_val, - ) - - call_kwargs = mock_build_sell.call_args - captured_min_sols.append( - call_kwargs.kwargs.get("min_sol_output", call_kwargs[1].get("min_sol_output")) - if call_kwargs.kwargs - else call_kwargs[1]["min_sol_output"] - ) - - # slippage=0 should give a higher min_sol than slippage=50 - assert captured_min_sols[0] > captured_min_sols[1] - - -@pytest.mark.asyncio -async def test_sell_token_partial_specific_amount(tmp_keystore): - """Sell a specific token amount (not 'all'), verify it parses to int(500000 * 10**6).""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - patch("pumpfun_cli.core.trade.build_sell_instructions") as mock_build_sell, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - # Balance check now queries token balance for specific amounts - client.get_token_account_balance.return_value = 500_000_000_000_000 - client.send_tx.return_value = "partialsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - mock_build_sell.return_value = [] - - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "500000", - ) - - assert result["action"] == "sell" - # 500000 * 10**6 = 500_000_000_000 raw token units - expected_amount = int(500000 * 10**6) - call_kwargs = mock_build_sell.call_args - actual_amount = ( - call_kwargs.kwargs.get("token_amount") - if call_kwargs.kwargs - else call_kwargs[1]["token_amount"] + assert result["quote_symbol"] == "SOL" + assert result["quote_spent"] == 0.01 + assert result["signature"] == "fakesig" + + sent = client.sent[0] + assert sent["compute_units"] == PUMP_BUY_COMPUTE_UNITS + # Only the base ATA is created; a SOL coin's quote account is seed-checked only. + assert [i.program_id for i in sent["instructions"]] == [ASSOCIATED_TOKEN_PROGRAM, PUMP_PROGRAM] + ix = _pump_ix(sent) + assert bytes(ix.data[:8]) == BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR + spend = int.from_bytes(bytes(ix.data[8:16]), "little") + floor = int.from_bytes(bytes(ix.data[16:24]), "little") + expected = 10_000_000 * 1_000_000_000_000_000 // (30_000_000_000 + 10_000_000) + assert spend == 10_000_000 + assert floor == int(expected * 0.9) + keys = [m.pubkey for m in ix.accounts] + assert keys[2] == WSOL_MINT + assert keys[16] == derive_creator_vault(CREATOR) + + +async def test_buy_uses_curve_creator_not_launcher(tmp_keystore): + """A fee-sharing or holder-reward coin stores a different creator; the vault follows it.""" + other = Pubkey.new_unique() + client = chain_with_coin(curve=build_curve_data(creator=other)) + with _patched(client): + await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert _pump_ix(client.sent[0]).accounts[16].pubkey == derive_creator_vault(other) + + +async def test_buy_full_slippage_still_asks_for_one_token(tmp_keystore): + client = chain_with_coin() + with _patched(client): + await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, slippage=100) + floor = int.from_bytes(bytes(_pump_ix(client.sent[0]).data[16:24]), "little") + assert floor == 1 + + +async def test_buy_non_sol_coin_scales_by_quote_decimals(tmp_keystore): + user = _user_from(tmp_keystore) + client = chain_with_coin( + curve=build_curve_data(quote_mint=T22_QUOTE, virtual_quote=5_000_000_000), + quote_mint=T22_QUOTE, + quote_decimals=6, ) - assert actual_amount == expected_amount - # get_token_account_balance is now called once for balance validation - client.get_token_account_balance.assert_called_once() - - -@pytest.mark.asyncio -async def test_buy_token_without_confirm(tmp_keystore): - """Buy without confirm flag — result should NOT have 'confirmed' key.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "noconfirmsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} + client.token_balances[find_ata(user, T22_QUOTE, TOKEN_2022_PROGRAM)] = 5_000_000 + with _patched(client): + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 2.5) + assert result["quote_symbol"] == str(T22_QUOTE) + sent = client.sent[0] + assert sent["compute_units"] == PUMP_NON_SOL_TRADE_COMPUTE_UNITS + ix = _pump_ix(sent) + assert int.from_bytes(bytes(ix.data[8:16]), "little") == 2_500_000 + assert ix.accounts[2].pubkey == T22_QUOTE + assert ix.accounts[4].pubkey == TOKEN_2022_PROGRAM + + +async def test_buy_non_sol_coin_needs_the_quote_asset(tmp_keystore): + client = chain_with_coin( + curve=build_curve_data(quote_mint=T22_QUOTE), quote_mint=T22_QUOTE, quote_decimals=6 + ) + with _patched(client): + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 2.5) + assert result["error"] == "insufficient_balance" + assert client.sent == [] - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - confirm=False, - ) - assert result["action"] == "buy" - assert "confirmed" not in result - - -@pytest.mark.asyncio -async def test_buy_token_with_confirm(tmp_keystore): - """Buy with confirm=True — result should have 'confirmed': True.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "confirmsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} +async def test_buy_insufficient_sol(tmp_keystore): + client = chain_with_coin() + client.default_balance = 1_000 + with _patched(client): + result = await buy_token("http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01) + assert result["error"] == "insufficient_balance" + assert client.sent == [] + +async def test_buy_dry_run_sends_nothing(tmp_keystore): + client = chain_with_coin() + with _patched(client): result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - confirm=True, + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, dry_run=True ) - - assert result["action"] == "buy" - assert result["confirmed"] is True - assert result["signature"] == "confirmsig" - # Verify confirm=True was passed to send_tx - client.send_tx.assert_called_once() - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("confirm") is True or ( - len(call_kwargs.args) > 2 and call_kwargs.args[2] is True + assert result["dry_run"] is True + assert result["quote_in"] == 0.01 + assert result["spot_price"] == pytest.approx(3e-8) # 30 SOL / 1e9 tokens + assert {"expected_tokens", "min_tokens_out", "effective_price", "price_impact_pct"} <= set( + result ) + assert client.sent == [] -# --- dry-run tests --- - - -@pytest.mark.asyncio -async def test_buy_token_dry_run(tmp_keystore): - """dry_run=True returns simulation dict without calling send_tx.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - +@pytest.mark.parametrize("code,error", [(6042, "slippage"), (6000, "tx_error")]) +async def test_buy_maps_onchain_errors(tmp_keystore, code, error): + client = chain_with_coin() + client.send_error = TransactionFailedError({"InstructionError": [2, {"Custom": code}]}) + with _patched(client): result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - dry_run=True, + "http://rpc", tmp_keystore, "testpass", MINT_STR, 0.01, confirm=True ) + assert result["error"] == error + assert result["error_code"] == code - assert result["dry_run"] is True - assert result["action"] == "buy" - assert result["venue"] == "bonding_curve" - assert result["sol_in"] == 0.01 - assert result["expected_tokens"] > 0 - assert result["effective_price_sol"] > 0 - assert result["spot_price_sol"] > 0 - assert "price_impact_pct" in result - assert "min_tokens_out" in result - assert "signature" not in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_sell_token_dry_run(tmp_keystore): - """dry_run=True returns simulation dict without calling send_tx.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - dry_run=True, - ) - assert result["dry_run"] is True - assert result["action"] == "sell" - assert result["venue"] == "bonding_curve" - assert result["tokens_in"] == 1.0 - assert result["expected_sol"] > 0 - assert result["effective_price_sol"] > 0 - assert result["spot_price_sol"] > 0 - assert "price_impact_pct" in result - assert "min_sol_out" in result - assert "signature" not in result - client.send_tx.assert_not_called() - - -# --- wrong password --- - - -# --- priority_fee / compute_units override tests --- - - -@pytest.mark.asyncio -async def test_buy_token_custom_priority_fee(tmp_keystore): - """Custom priority_fee and compute_units are forwarded to send_tx.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.return_value = "sig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - - await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - priority_fee=999_999, - compute_units=150_000, - ) +# --- sell --- - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("priority_fee") == 999_999 - assert call_kwargs.kwargs.get("compute_units") == 150_000 - - -@pytest.mark.asyncio -async def test_sell_token_custom_priority_fee(tmp_keystore): - """Custom priority_fee and compute_units are forwarded to send_tx for sell.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.send_tx.return_value = "sig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - - await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - priority_fee=42_000, - compute_units=200_000, - ) - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("priority_fee") == 42_000 - assert call_kwargs.kwargs.get("compute_units") == 200_000 - - -@pytest.mark.asyncio -async def test_migrate_custom_priority_fee(tmp_keystore): - """Custom priority_fee and compute_units are forwarded to send_tx for migrate.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - ): - client = AsyncMock() - MockClient.return_value = client - bc_resp = MagicMock() - bc_resp.value = MagicMock() - bc_resp.value.data = b"\x00" * 200 - global_resp = MagicMock() - global_resp.value = MagicMock() - global_resp.value.data = b"\x00" * 200 - client.get_account_info.side_effect = [bc_resp, global_resp] - client.send_tx.return_value = "migsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.side_effect = [ - {"complete": True}, - {"withdraw_authority": bytes(Pubkey.from_string("11111111111111111111111111111112"))}, - ] - idl.get_instruction_discriminators.return_value = {"migrate": b"\x00" * 8} - - await migrate_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - priority_fee=77_000, - compute_units=500_000, - ) +async def test_sell_all(tmp_keystore): + user = _user_from(tmp_keystore) + client = chain_with_coin() + client.token_balances[find_ata(user, COIN, TOKEN_2022_PROGRAM)] = 5_000_000 + with _patched(client): + result = await sell_token("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + assert result["tokens_sold"] == 5.0 + assert result["quote_symbol"] == "SOL" + ix = _pump_ix(client.sent[0]) + assert bytes(ix.data[:8]) == SELL_V2_DISCRIMINATOR + assert len(ix.accounts) == 26 + assert int.from_bytes(bytes(ix.data[8:16]), "little") == 5_000_000 + # A SOL coin pays out natively, so only the sell instruction is sent. + assert len(client.sent[0]["instructions"]) == 1 - call_kwargs = client.send_tx.call_args - assert call_kwargs.kwargs.get("priority_fee") == 77_000 - assert call_kwargs.kwargs.get("compute_units") == 500_000 +async def test_sell_non_sol_creates_the_payout_account(tmp_keystore): + user = _user_from(tmp_keystore) + client = chain_with_coin( + curve=build_curve_data(quote_mint=T22_QUOTE), quote_mint=T22_QUOTE, quote_decimals=6 + ) + client.token_balances[find_ata(user, COIN, TOKEN_2022_PROGRAM)] = 1_000_000 + with _patched(client): + result = await sell_token("http://rpc", tmp_keystore, "testpass", MINT_STR, "1") + assert result["action"] == "sell" + instructions = client.sent[0]["instructions"] + assert instructions[0].program_id == ASSOCIATED_TOKEN_PROGRAM + assert instructions[0].accounts[3].pubkey == T22_QUOTE -@pytest.mark.asyncio -async def test_buy_wrong_password(tmp_keystore): - with pytest.raises(ValueError, match="password"): - await buy_token( - "http://rpc", - tmp_keystore, - "wrongpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +async def test_sell_more_than_held(tmp_keystore): + client = chain_with_coin() + with _patched(client): + result = await sell_token("http://rpc", tmp_keystore, "testpass", MINT_STR, "5") + assert result["error"] == "insufficient_balance" -# --- pre-trade balance validation tests --- - - -@pytest.mark.asyncio -async def test_buy_token_insufficient_sol(tmp_keystore): - """Buy with wallet balance too low returns insufficient_balance without calling send_tx.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 1_000 # very low balance - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +async def test_sell_nothing_held(tmp_keystore): + client = chain_with_coin() + with _patched(client): + result = await sell_token("http://rpc", tmp_keystore, "testpass", MINT_STR, "all") + assert result["error"] == "no_tokens" - assert result["error"] == "insufficient_balance" - assert "available_sol" in result - assert "required_sol" in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_sell_token_insufficient_tokens(tmp_keystore): - """Sell a specific amount exceeding token balance returns insufficient_balance.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 500_000 # 0.5 tokens at 6 decimals - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } +async def test_sell_dry_run(tmp_keystore): + user = _user_from(tmp_keystore) + client = chain_with_coin() + client.token_balances[find_ata(user, COIN, TOKEN_2022_PROGRAM)] = 5_000_000 + with _patched(client): result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1000", # asking to sell 1000 tokens but only have 0.5 - ) - - assert result["error"] == "insufficient_balance" - assert "available_tokens" in result - assert "required_tokens" in result - client.send_tx.assert_not_called() - - -@pytest.mark.asyncio -async def test_buy_token_sufficient_sol_proceeds(tmp_keystore): - """Buy with adequate balance proceeds normally (regression guard).""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 # 10 SOL — plenty - client.send_tx.return_value = "sufficientsig" - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, + "http://rpc", tmp_keystore, "testpass", MINT_STR, "all", dry_run=True ) + assert result["dry_run"] is True + assert {"expected_quote", "min_quote_out", "spot_price"} <= set(result) + assert client.sent == [] - assert result["action"] == "buy" - assert result["signature"] == "sufficientsig" - client.send_tx.assert_called_once() - - -# --- TransactionFailedError / slippage tests --- - - -@pytest.mark.asyncio -async def test_buy_token_slippage_error_on_confirm(tmp_keystore): - """When send_tx raises TransactionFailedError with slippage code, buy returns slippage error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6002))))" - ) - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +# --- cashback, accumulator, fees, migration --- - assert result["error"] == "slippage" - assert result["error_code"] == 6002 - - -@pytest.mark.asyncio -async def test_sell_token_slippage_error_on_confirm(tmp_keystore): - """When send_tx raises TransactionFailedError with slippage code 6003, sell returns slippage.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6003))))" - ) - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - ) +async def test_claim_cashback_without_accumulator(tmp_keystore): + client = FakeClient() + with _patched(client): + result = await claim_cashback("http://rpc", tmp_keystore, "testpass") + assert result["error"] == "no_accumulator" - assert result["error"] == "slippage" - assert result["error_code"] == 6003 - - -@pytest.mark.asyncio -async def test_buy_token_non_slippage_tx_error(tmp_keystore): - """Non-slippage error code returns tx_error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6020))))" - ) - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) +async def test_claim_cashback_sol(tmp_keystore): + user = _user_from(tmp_keystore) + client = FakeClient() + client.add(find_user_volume_accumulator(user), b"\x00" * 8) + with _patched(client): + result = await claim_cashback("http://rpc", tmp_keystore, "testpass") + assert result["quote_symbol"] == "SOL" + ixs = client.sent[0]["instructions"] + assert len(ixs) == 1 + assert bytes(ixs[0].data) == CLAIM_CASHBACK_V2_DISCRIMINATOR + + +async def test_claim_cashback_other_quote_creates_payout_account(tmp_keystore): + user = _user_from(tmp_keystore) + client = FakeClient() + client.add_mint(T22_QUOTE, 6) + client.add(find_user_volume_accumulator(user), b"\x00" * 8) + with _patched(client): + await claim_cashback("http://rpc", tmp_keystore, "testpass", str(T22_QUOTE)) + ixs = client.sent[0]["instructions"] + assert ixs[0].program_id == ASSOCIATED_TOKEN_PROGRAM + assert ixs[1].accounts[2].pubkey == T22_QUOTE + + +async def test_close_volume_accumulator(tmp_keystore): + client = FakeClient() + with _patched(client): + result = await close_volume_accumulator("http://rpc", tmp_keystore, "testpass") + assert result["action"] == "close_volume_accumulator" + assert result["signature"] == "fakesig" - assert result["error"] == "tx_error" - assert result["error_code"] == 6020 - - -@pytest.mark.asyncio -async def test_sell_token_non_slippage_tx_error(tmp_keystore): - """Non-slippage error code on sell returns tx_error.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_token_account_balance.return_value = 1_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6021))))" - ) - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - "is_cashback_coin": False, - } - idl.get_instruction_discriminators.return_value = {"sell": b"\x00" * 8} - result = await sell_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "1.0", - ) +async def test_collect_creator_fees_nothing_accrued(tmp_keystore): + user = _user_from(tmp_keystore) + client = FakeClient() + client.balances[derive_creator_vault(user)] = 0 + with _patched(client): + result = await collect_creator_fees("http://rpc", tmp_keystore, "testpass") + assert result["error"] == "no_fees" - assert result["error"] == "tx_error" - assert result["error_code"] == 6021 - - -@pytest.mark.asyncio -async def test_buy_token_client_closed_on_tx_error(tmp_keystore): - """client.close() is called even when TransactionFailedError is raised.""" - from pumpfun_cli.protocol.client import TransactionFailedError - - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 10_000_000_000 - client.send_tx.side_effect = TransactionFailedError( - "TransactionErrorInstructionError((0, Tagged(InstructionErrorCustom(6002))))" - ) - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } - idl.get_instruction_discriminators.return_value = {"buy": b"\x00" * 8} - - await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - ) - client.close.assert_called_once() - - -@pytest.mark.asyncio -async def test_buy_token_dry_run_insufficient_sol_includes_warning(tmp_keystore): - """Dry-run with low balance still returns simulation but includes balance_warning.""" - with ( - patch("pumpfun_cli.core.trade.RpcClient") as MockClient, - patch("pumpfun_cli.core.trade.IDLParser") as MockIDL, - _PATCH_TOKEN_PROG, - ): - client = AsyncMock() - MockClient.return_value = client - resp = MagicMock() - resp.value = MagicMock() - resp.value.data = b"\x00" * 200 - client.get_account_info.return_value = resp - client.get_balance.return_value = 1_000 # very low - client.close = AsyncMock() - - idl = MagicMock() - MockIDL.return_value = idl - idl.decode_account_data.return_value = { - "complete": False, - "virtual_token_reserves": 1_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 10_000_000_000, - "creator": bytes(Pubkey.from_string("11111111111111111111111111111112")), - "is_mayhem_mode": False, - } +async def test_collect_creator_fees_from_both_venues(tmp_keystore): + user = _user_from(tmp_keystore) + client = FakeClient() + client.balances[derive_creator_vault(user)] = 2_000_650_240 + client.token_balances[find_ata(derive_amm_creator_vault(user), WSOL_MINT, TOKEN_PROGRAM)] = ( + 500_000_000 + ) + with _patched(client): + result = await collect_creator_fees("http://rpc", tmp_keystore, "testpass") + assert result["pump_recovered"] == 2.0 + assert result["pumpswap_recovered"] == 0.5 + discs = [bytes(i.data[:8]) for i in client.sent[0]["instructions"]] + assert COLLECT_CREATOR_FEE_V2_DISCRIMINATOR in discs + assert COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR in discs + + +async def test_migrate_needs_a_complete_curve(tmp_keystore): + client = chain_with_coin() + with _patched(client): + result = await migrate_token("http://rpc", tmp_keystore, "testpass", MINT_STR) + assert result["error"] == "not_complete" - result = await buy_token( - "http://rpc", - tmp_keystore, - "testpass", - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - 0.01, - dry_run=True, - ) - assert result["dry_run"] is True - assert "balance_warning" in result - client.send_tx.assert_not_called() +async def test_migrate_complete_curve_uses_global_withdraw_authority(tmp_keystore): + client = chain_with_coin(curve=build_curve_data(complete=True)) + with _patched(client): + result = await migrate_token("http://rpc", tmp_keystore, "testpass", MINT_STR) + assert result["action"] == "migrate" + ix = client.sent[0]["instructions"][0] + assert bytes(ix.data) == MIGRATE_V2_DISCRIMINATOR + assert ix.accounts[1].pubkey == GLOBAL_DEFAULTS["withdraw_authority"] + assert ix.accounts[10].pubkey == derive_amm_pool(COIN, WSOL_MINT) + + +async def test_collect_ignores_the_vaults_rent_floor(tmp_keystore): + """A SOL creator vault keeps its rent-exempt minimum; that is not a fee to collect.""" + user = _user_from(tmp_keystore) + client = FakeClient() + client.balances[derive_creator_vault(user)] = 650_240 + with _patched(client): + result = await collect_creator_fees("http://rpc", tmp_keystore, "testpass") + assert result["error"] == "no_fees" + assert client.sent == [] diff --git a/tests/test_protocol/test_address.py b/tests/test_protocol/test_address.py index d18fa99..90649eb 100644 --- a/tests/test_protocol/test_address.py +++ b/tests/test_protocol/test_address.py @@ -1,22 +1,47 @@ +"""PDA derivations that no builder test reaches through an IDL seed spec.""" + from solders.pubkey import Pubkey +from spl.token.instructions import get_associated_token_address from pumpfun_cli.protocol.address import ( - derive_bonding_curve, - derive_bonding_curve_v2, - derive_creator_vault, + derive_amm_pool, + derive_pool_authority, + find_ata, + find_holder_rewards, + find_quote_control, +) +from pumpfun_cli.protocol.contracts import ( + PUMP_AMM_PROGRAM, + PUMP_PROGRAM, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + USDC_MINT, + WSOL_MINT, ) +MINT = Pubkey.new_unique() +OWNER = Pubkey.new_unique() + -def test_bonding_curve_is_deterministic(): - mint = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - assert derive_bonding_curve(mint) == derive_bonding_curve(mint) +def test_find_ata_matches_spl_for_both_token_programs(): + for program in (TOKEN_PROGRAM, TOKEN_2022_PROGRAM): + assert find_ata(OWNER, MINT, program) == get_associated_token_address(OWNER, MINT, program) + assert find_ata(OWNER, MINT, TOKEN_PROGRAM) != find_ata(OWNER, MINT, TOKEN_2022_PROGRAM) -def test_bonding_curve_v2_differs_from_v1(): - mint = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - assert derive_bonding_curve(mint) != derive_bonding_curve_v2(mint) +def test_canonical_pool_depends_on_quote_mint(): + authority = derive_pool_authority(MINT) + expected = Pubkey.find_program_address( + [b"pool", bytes([0, 0]), bytes(authority), bytes(MINT), bytes(USDC_MINT)], + PUMP_AMM_PROGRAM, + )[0] + assert derive_amm_pool(MINT, USDC_MINT) == expected + assert derive_amm_pool(MINT) == derive_amm_pool(MINT, WSOL_MINT) != expected -def test_creator_vault_deterministic(): - user = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - assert derive_creator_vault(user) == derive_creator_vault(user) +def test_holder_rewards_and_quote_control_seeds(): + assert ( + find_holder_rewards(MINT) + == Pubkey.find_program_address([b"holder-rewards", bytes(MINT)], PUMP_PROGRAM)[0] + ) + assert find_quote_control() == Pubkey.find_program_address([b"quote-control"], PUMP_PROGRAM)[0] diff --git a/tests/test_protocol/test_client.py b/tests/test_protocol/test_client.py index 26fddde..de742f3 100644 --- a/tests/test_protocol/test_client.py +++ b/tests/test_protocol/test_client.py @@ -54,3 +54,137 @@ def test_transaction_failed_error_inherits_runtime_error(): exc = TransactionFailedError("test") assert isinstance(exc, RuntimeError) + + +# --- transaction version selection --- + + +def _ixs(n_accounts: int): + from solders.instruction import AccountMeta, Instruction + from solders.pubkey import Pubkey + + program = Pubkey.new_unique() + metas = [AccountMeta(Pubkey.new_unique(), False, True) for _ in range(n_accounts)] + return [Instruction(program, b"\x00" * 16, metas)] + + +def test_small_transaction_compiles_as_v0(): + from solders.hash import Hash + from solders.message import MessageV0 + from solders.pubkey import Pubkey + + from pumpfun_cli.protocol.client import build_message + + msg = build_message(Pubkey.new_unique(), _ixs(10), Hash.default(), 100_000, 200_000) + assert isinstance(msg, MessageV0) + + +def test_oversized_transaction_compiles_as_v1_with_budget_in_header(): + from solders.hash import Hash + from solders.message import MessageV1 + from solders.pubkey import Pubkey + + from pumpfun_cli.protocol.client import TX_V0_MAX_BYTES, build_message, transaction_size + + msg = build_message(Pubkey.new_unique(), _ixs(40), Hash.default(), 300_000, 200_000) + assert isinstance(msg, MessageV1) + assert transaction_size(msg) > TX_V0_MAX_BYTES + assert len(msg.instructions) == 1 # no ComputeBudget instructions in a v1 message + + +def test_transaction_beyond_v1_limit_is_refused(): + from solders.hash import Hash + from solders.pubkey import Pubkey + + from pumpfun_cli.protocol.client import build_message + + with pytest.raises(ValueError): + build_message(Pubkey.new_unique(), _ixs(125), Hash.default(), 300_000, 200_000) + + +def test_transaction_failed_error_parses_json_rpc_shape(): + from pumpfun_cli.protocol.client import TransactionFailedError + + exc = TransactionFailedError({"InstructionError": [3, {"Custom": 6042}]}) + assert (exc.instruction_index, exc.error_code) == (3, 6042) + + +# --- getTransaction is read as raw JSON, whatever the transaction version --- + +_SIG = "5" * 87 + + +@pytest.mark.asyncio +async def test_get_transaction_reads_v1_result(respx_mock): + import httpx + + respx_mock.post("https://rpc.test").mock( + return_value=httpx.Response( + 200, + json={ + "jsonrpc": "2.0", + "id": 1, + "result": { + "slot": 7, + "blockTime": 99, + "version": 1, + "transaction": ["AAAA", "base64"], + "meta": {"err": None, "fee": 5000}, + }, + }, + ) + ) + client = RpcClient("https://rpc.test") + try: + result = await client.get_transaction(_SIG) + finally: + await client.close() + assert result == {"slot": 7, "err": None, "fee": 5000, "block_time": 99} + body = respx_mock.calls.last.request.content + assert b'"maxSupportedTransactionVersion":1' in body.replace(b" ", b"") + + +@pytest.mark.asyncio +async def test_get_transaction_not_found(respx_mock): + import httpx + + respx_mock.post("https://rpc.test").mock( + return_value=httpx.Response(200, json={"jsonrpc": "2.0", "id": 1, "result": None}) + ) + client = RpcClient("https://rpc.test") + try: + assert await client.get_transaction(_SIG) is None + finally: + await client.close() + + +@pytest.mark.asyncio +async def test_get_transaction_http_error_hides_the_url(respx_mock): + import httpx + + url = "https://rpc.test/secret-api-key" + respx_mock.post(url).mock(return_value=httpx.Response(429)) + client = RpcClient(url) + try: + with pytest.raises(RuntimeError) as info: + await client.get_transaction(_SIG) + finally: + await client.close() + assert "429" in str(info.value) + assert "secret-api-key" not in str(info.value) + assert info.value.__cause__ is None and info.value.__context__ is None + + +@pytest.mark.asyncio +async def test_failed_confirmation_reports_the_signature(): + from unittest.mock import AsyncMock, MagicMock + + from solders.signature import Signature + + sig = Signature.default() + client = RpcClient("https://rpc.test") + client.rpc = MagicMock() + client.rpc.send_transaction = AsyncMock(return_value=MagicMock(value=sig)) + client.rpc.confirm_transaction = AsyncMock(side_effect=TimeoutError()) + with pytest.raises(RuntimeError, match=str(sig)): + await client._send_and_confirm(MagicMock(), confirm=True) diff --git a/tests/test_protocol/test_curve.py b/tests/test_protocol/test_curve.py index 077c3dc..93b5cc8 100644 --- a/tests/test_protocol/test_curve.py +++ b/tests/test_protocol/test_curve.py @@ -1,50 +1,55 @@ -from pumpfun_cli.protocol.curve import ( - calculate_buy_tokens_out, - calculate_sell_sol_out, - get_bonding_progress, - get_token_price_sol, -) +"""BondingCurve decoding at every account length.""" +import pytest +from solders.pubkey import Pubkey -def _mock_state(): - return { - "virtual_token_reserves": 1_073_000_000_000_000, - "virtual_sol_reserves": 30_000_000_000, - "real_sol_reserves": 0, - "real_token_reserves": 793_100_000_000_000, - "token_total_supply": 1_000_000_000_000_000, - "complete": False, - } +from pumpfun_cli.protocol.contracts import USDC_MINT, WSOL_MINT +from pumpfun_cli.protocol.curve import parse_bonding_curve +from tests.test_core.helpers import CREATOR, build_curve_data -def test_buy_returns_positive_tokens(): - assert calculate_buy_tokens_out(_mock_state(), 100_000_000) > 0 +@pytest.mark.parametrize("length", [125, 151, 256]) +def test_parses_every_allocation_size(length): + state = parse_bonding_curve( + build_curve_data(length=length, creator_fee_bps=100, holder_reward=True, mayhem=True) + ) + assert state["virtual_quote_reserves"] == 30_000_000_000 + assert state["creator"] == CREATOR + assert state["is_mayhem_mode"] is True + assert state["creator_fee_bps"] == 100 + assert state["is_holder_reward"] is True -def test_sell_returns_positive_sol(): - assert calculate_sell_sol_out(_mock_state(), 1_000_000_000) > 0 +def test_sol_paired_curve_normalizes_to_wsol(): + state = parse_bonding_curve(build_curve_data()) + assert state["quote_mint"] == WSOL_MINT + assert state["is_sol_paired"] is True -def test_buy_sell_roundtrip_loses_value(): - state = _mock_state() - tokens = calculate_buy_tokens_out(state, 100_000_000) - state["virtual_sol_reserves"] += 100_000_000 - state["virtual_token_reserves"] -= tokens - sol_back = calculate_sell_sol_out(state, tokens) - assert sol_back < 100_000_000 +def test_non_sol_quote_mint_is_kept(): + state = parse_bonding_curve(build_curve_data(quote_mint=USDC_MINT)) + assert state["quote_mint"] == USDC_MINT + assert state["is_sol_paired"] is False -def test_price_is_positive(): - assert get_token_price_sol(_mock_state()) > 0 +def test_short_curve_reports_new_fields_as_absent(): + state = parse_bonding_curve(build_curve_data(length=115)) + assert state["creator_fee_bps"] is None + assert state["is_holder_reward"] is None -def test_bonding_progress_zero_at_start(): - state = _mock_state() - state["real_sol_reserves"] = 0 - assert get_bonding_progress(state) == 0.0 +def test_curve_without_flags_is_refused(): + with pytest.raises(ValueError): + parse_bonding_curve(build_curve_data(length=82)) -def test_bonding_progress_capped_at_one(): - state = _mock_state() - state["real_sol_reserves"] = 100 * 1_000_000_000 - assert get_bonding_progress(state) == 1.0 +def test_wrong_discriminator_is_refused(): + data = bytearray(build_curve_data()) + data[0] ^= 0xFF + with pytest.raises(ValueError): + parse_bonding_curve(bytes(data)) + + +def test_holder_reward_curve_creator_is_read_as_stored(): + pda = Pubkey.new_unique() + assert parse_bonding_curve(build_curve_data(creator=pda))["creator"] == pda diff --git a/tests/test_protocol/test_curve_extra.py b/tests/test_protocol/test_curve_extra.py index a0c1132..343819e 100644 --- a/tests/test_protocol/test_curve_extra.py +++ b/tests/test_protocol/test_curve_extra.py @@ -1,23 +1,44 @@ -"""Additional curve tests for edge cases and missing coverage.""" +"""Curve math: constant-product quotes, price and graduation progress.""" + +import pytest from pumpfun_cli.protocol.curve import ( - get_token_price_sol, + calculate_buy_tokens_out, + calculate_sell_quote_out, + get_bonding_progress, + get_token_price, is_graduated, ) +STATE = { + "virtual_token_reserves": 1_000_000_000_000_000, + "virtual_quote_reserves": 30_000_000_000, + "real_token_reserves": 700_000_000_000_000, + "complete": False, +} + + +def test_buy_and_sell_are_constant_product(): + tokens = calculate_buy_tokens_out(STATE, 1_000_000_000) + assert tokens == 1_000_000_000 * 1_000_000_000_000_000 // 31_000_000_000 + assert calculate_sell_quote_out(STATE, tokens) < 1_000_000_000 + -def test_get_token_price_sol_zero_reserves(): - state = {"virtual_token_reserves": 0, "virtual_sol_reserves": 30_000_000_000} - assert get_token_price_sol(state) == 0.0 +@pytest.mark.parametrize("decimals,expected", [(9, 3e-8), (6, 3e-5)]) +def test_price_scales_by_quote_decimals(decimals, expected): + assert get_token_price(STATE, decimals) == pytest.approx(expected) -def test_is_graduated_true(): - assert is_graduated({"complete": True}) is True +def test_price_of_empty_curve_is_zero(): + assert get_token_price({**STATE, "virtual_token_reserves": 0}, 9) == 0.0 -def test_is_graduated_false(): - assert is_graduated({"complete": False}) is False +def test_progress_against_global_opening_reserves(): + assert get_bonding_progress(STATE, 800_000_000_000_000) == pytest.approx(0.125) + assert get_bonding_progress({**STATE, "complete": True}, 800_000_000_000_000) == 1.0 + assert get_bonding_progress(STATE, 0) == 0.0 -def test_is_graduated_zero(): - assert is_graduated({"complete": 0}) is False +def test_is_graduated(): + assert is_graduated({"complete": True}) + assert not is_graduated({"complete": False}) diff --git a/tests/test_protocol/test_extras_instructions.py b/tests/test_protocol/test_extras_instructions.py index 4449d77..b95a348 100644 --- a/tests/test_protocol/test_extras_instructions.py +++ b/tests/test_protocol/test_extras_instructions.py @@ -1,95 +1,82 @@ -"""Tests for claim-cashback, close-volume-acc, and migrate instruction builders.""" - -from pathlib import Path +"""Payout, cleanup and migration builders follow the quote asset they act on.""" from solders.pubkey import Pubkey +from pumpfun_cli.protocol.address import ( + derive_amm_creator_vault, + derive_amm_lp_mint, + derive_amm_pool, + derive_creator_vault, + find_ata, + find_user_volume_accumulator, +) from pumpfun_cli.protocol.contracts import ( - COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR, - COLLECT_CREATOR_FEE_DISCRIMINATOR, - PUMP_AMM_PROGRAM, - PUMP_PROGRAM, + RENT, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + WSOL_MINT, ) -from pumpfun_cli.protocol.idl_parser import IDLParser from pumpfun_cli.protocol.instructions import ( - build_claim_cashback_instruction, - build_close_volume_accumulator_instruction, + build_claim_cashback_v2_instruction, build_collect_coin_creator_fee_instruction, - build_collect_creator_fee_instruction, - build_migrate_instruction, + build_collect_creator_fee_v2_instruction, + build_migrate_v2_instruction, ) -IDL_PATH = Path(__file__).parent.parent.parent / "idl" / "pump_fun_idl.json" -_USER = Pubkey.from_string("11111111111111111111111111111112") -_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") -_WA = Pubkey.from_string("11111111111111111111111111111113") - - -def test_claim_cashback_has_5_accounts(): - idl = IDLParser(str(IDL_PATH)) - ix = build_claim_cashback_instruction(idl=idl, user=_USER) - assert len(ix.accounts) == 5 - assert ix.program_id == PUMP_PROGRAM - assert ix.accounts[0].pubkey == _USER - assert ix.accounts[0].is_signer is False - assert ix.accounts[0].is_writable is True - - -def test_close_volume_accumulator_has_4_accounts(): - idl = IDLParser(str(IDL_PATH)) - ix = build_close_volume_accumulator_instruction(idl=idl, user=_USER) - assert len(ix.accounts) == 4 - assert ix.program_id == PUMP_PROGRAM - assert ix.accounts[0].pubkey == _USER - assert ix.accounts[0].is_signer is True - - -def test_migrate_has_24_accounts(): - idl = IDLParser(str(IDL_PATH)) - ix = build_migrate_instruction(idl=idl, mint=_MINT, user=_USER, withdraw_authority=_WA) - assert len(ix.accounts) == 24 - assert ix.program_id == PUMP_PROGRAM - - -def test_migrate_user_is_signer(): - idl = IDLParser(str(IDL_PATH)) - ix = build_migrate_instruction(idl=idl, mint=_MINT, user=_USER, withdraw_authority=_WA) - user_account = ix.accounts[5] - assert user_account.pubkey == _USER - assert user_account.is_signer is True - - -def test_migrate_includes_amm_program(): - idl = IDLParser(str(IDL_PATH)) - ix = build_migrate_instruction(idl=idl, mint=_MINT, user=_USER, withdraw_authority=_WA) - account_pubkeys = [a.pubkey for a in ix.accounts] - assert PUMP_AMM_PROGRAM in account_pubkeys - - -def test_collect_creator_fee_has_5_accounts(): - idl = IDLParser(str(IDL_PATH)) - ix = build_collect_creator_fee_instruction(idl=idl, creator=_USER) - assert len(ix.accounts) == 5 - assert ix.program_id == PUMP_PROGRAM - assert ix.data[:8] == COLLECT_CREATOR_FEE_DISCRIMINATOR - - -def test_claim_cashback_user_not_signer(): - """claim_cashback account[0] (user) must be is_signer=False per IDL.""" - idl = IDLParser(str(IDL_PATH)) - ix = build_claim_cashback_instruction(idl=idl, user=_USER) - assert ix.accounts[0].pubkey == _USER - assert ix.accounts[0].is_signer is False - - -def test_collect_coin_creator_fee_has_8_accounts(): - ix = build_collect_coin_creator_fee_instruction(creator=_USER) - assert len(ix.accounts) == 8 - assert ix.program_id == PUMP_AMM_PROGRAM - assert ix.data[:8] == COLLECT_COIN_CREATOR_FEE_DISCRIMINATOR - - -def test_collect_coin_creator_fee_last_account_is_program(): - """collect_coin_creator_fee 8th account must be PUMP_AMM_PROGRAM.""" - ix = build_collect_coin_creator_fee_instruction(creator=_USER) - assert ix.accounts[7].pubkey == PUMP_AMM_PROGRAM +USER = Pubkey.new_unique() +MINT = Pubkey.new_unique() +QUOTE = Pubkey.new_unique() + + +def _keys(ix): + return [m.pubkey for m in ix.accounts] + + +def test_claim_cashback_v2_uses_the_accumulators_quote_account(): + keys = _keys( + build_claim_cashback_v2_instruction( + user=USER, quote_mint=QUOTE, quote_token_program=TOKEN_2022_PROGRAM + ) + ) + acc = find_user_volume_accumulator(USER) + assert keys[2:4] == [QUOTE, TOKEN_2022_PROGRAM] + assert keys[5] == find_ata(acc, QUOTE, TOKEN_2022_PROGRAM) + assert keys[6] == find_ata(USER, QUOTE, TOKEN_2022_PROGRAM) + + +def test_collect_creator_fee_v2_per_quote_vault(): + keys = _keys( + build_collect_creator_fee_v2_instruction( + creator=USER, quote_mint=QUOTE, quote_token_program=TOKEN_2022_PROGRAM + ) + ) + vault = derive_creator_vault(USER) + assert keys[1] == find_ata(USER, QUOTE, TOKEN_2022_PROGRAM) + assert keys[2] == vault + assert keys[3] == find_ata(vault, QUOTE, TOKEN_2022_PROGRAM) + + +def test_collect_coin_creator_fee_defaults_to_wsol(): + keys = _keys(build_collect_coin_creator_fee_instruction(creator=USER)) + authority = derive_amm_creator_vault(USER) + assert keys[0:2] == [WSOL_MINT, TOKEN_PROGRAM] + assert keys[4] == find_ata(authority, WSOL_MINT, TOKEN_PROGRAM) + assert keys[5] == find_ata(USER, WSOL_MINT, TOKEN_PROGRAM) + + +def test_migrate_v2_targets_the_quote_specific_pool(): + keys = _keys( + build_migrate_v2_instruction( + mint=MINT, + user=USER, + withdraw_authority=Pubkey.new_unique(), + quote_mint=QUOTE, + quote_token_program=TOKEN_2022_PROGRAM, + ) + ) + pool = derive_amm_pool(MINT, QUOTE) + assert keys[3] == QUOTE + assert keys[10] == pool + assert keys[15] == derive_amm_lp_mint(pool) + assert keys[18] == find_ata(pool, QUOTE, TOKEN_2022_PROGRAM) + assert keys[24] == RENT diff --git a/tests/test_protocol/test_instructions.py b/tests/test_protocol/test_instructions.py index 7d3e713..d8745cc 100644 --- a/tests/test_protocol/test_instructions.py +++ b/tests/test_protocol/test_instructions.py @@ -1,230 +1,309 @@ +"""Instruction builders checked against the vendored IDLs. + +Every pump.fun builder must match its IDL instruction: discriminator, account +order, writable and signer flags, and fixed addresses. PumpSwap swaps carry +accounts the IDL does not list, so only their IDL prefix is checked here; the +extras are covered in test_pumpswap_instructions.py. +""" + +import json +import struct from pathlib import Path +import pytest +from solders.instruction import Instruction from solders.pubkey import Pubkey -from pumpfun_cli.protocol.address import derive_associated_bonding_curve, derive_bonding_curve +from pumpfun_cli.protocol import instructions as ix +from pumpfun_cli.protocol.address import ( + derive_bonding_curve, + derive_creator_vault, + find_ata, + find_global_volume_accumulator, + find_sharing_config, +) from pumpfun_cli.protocol.contracts import ( - BUY_EXACT_SOL_IN_DISCRIMINATOR, - MAYHEM_GLOBAL_PARAMS, - MAYHEM_PROGRAM_ID, - MAYHEM_SOL_VAULT, + BUYBACK_FEE_RECIPIENTS, + NORMAL_FEE_RECIPIENTS, + RESERVED_FEE_RECIPIENTS, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + WSOL_MINT, ) -from pumpfun_cli.protocol.idl_parser import IDLParser -from pumpfun_cli.protocol.instructions import ( - build_buy_exact_sol_in_instructions, - build_buy_instructions, - build_create_instructions, - build_extend_account_instruction, - build_sell_instructions, + +IDL_DIR = Path(__file__).parents[2] / "idl" +PUMP_IDL = json.loads((IDL_DIR / "pump_fun_idl.json").read_text()) +AMM_IDL = json.loads((IDL_DIR / "pump_fun_amm_idl.json").read_text()) + +USER = Pubkey.new_unique() +MINT = Pubkey.new_unique() +CREATOR = Pubkey.new_unique() + + +def _spec(idl: dict, name: str) -> dict: + return next(i for i in idl["instructions"] if i["name"] == name) + + +def assert_matches_idl(idl: dict, name: str, built: Instruction, prefix: int | None = None): + spec = _spec(idl, name) + assert bytes(built.data[:8]) == bytes(spec["discriminator"]), f"{name} discriminator" + accounts = spec["accounts"][:prefix] if prefix else spec["accounts"] + if prefix is None: + assert len(built.accounts) >= len(accounts) + for i, (want, got) in enumerate(zip(accounts, built.accounts, strict=False)): + label = f"{name}[{i}] {want['name']}" + assert got.is_writable == bool(want.get("writable")), f"{label} writable" + assert got.is_signer == bool(want.get("signer")), f"{label} signer" + if "address" in want: + assert got.pubkey == Pubkey.from_string(want["address"]), f"{label} address" + + +PUMP_BUILDERS = [ + ( + "buy_v2", + lambda: ix.build_buy_v2_instruction( + mint=MINT, creator=CREATOR, user=USER, token_amount=1, max_quote_cost=2 + ), + 27, + ), + ( + "buy_exact_quote_in_v2", + lambda: ix.build_buy_exact_quote_in_v2_instruction( + mint=MINT, creator=CREATOR, user=USER, spendable_quote_in=1, min_tokens_out=2 + ), + 27, + ), + ( + "sell_v2", + lambda: ix.build_sell_v2_instruction( + mint=MINT, creator=CREATOR, user=USER, token_amount=1, min_quote_out=2 + ), + 26, + ), + ( + "create_v2", + lambda: ix.build_create_v2_instruction( + mint=MINT, user=USER, creator=USER, name="n", symbol="s", uri="u" + ), + 20, + ), + ("extend_account", lambda: ix.build_extend_account_instruction(MINT, USER), 5), + ("claim_cashback_v2", lambda: ix.build_claim_cashback_v2_instruction(user=USER), 10), + ( + "close_user_volume_accumulator", + lambda: ix.build_close_volume_accumulator_instruction(USER), + 4, + ), + ( + "collect_creator_fee_v2", + lambda: ix.build_collect_creator_fee_v2_instruction(creator=USER), + 10, + ), + ( + "migrate_v2", + lambda: ix.build_migrate_v2_instruction(mint=MINT, user=USER, withdraw_authority=CREATOR), + 27, + ), +] + + +@pytest.mark.parametrize("name,build,count", PUMP_BUILDERS, ids=[b[0] for b in PUMP_BUILDERS]) +def test_pump_builders_match_idl(name, build, count): + built = build() + assert_matches_idl(PUMP_IDL, name, built) + assert len(built.accounts) == count + + +@pytest.mark.parametrize( + "name,build", + [ + ( + "collect_coin_creator_fee", + lambda: ix.build_collect_coin_creator_fee_instruction(creator=USER), + ), + ("init_user_volume_accumulator", lambda: ix.build_init_amm_user_volume_accumulator(USER)), + ], ) +def test_amm_builders_match_idl(name, build): + built = build() + assert_matches_idl(AMM_IDL, name, built) + assert len(built.accounts) == len(_spec(AMM_IDL, name)["accounts"]) -# create_v2 must always have exactly 16 accounts (including mayhem accounts). -_EXPECTED_CREATE_V2_ACCOUNTS = 16 - -IDL_PATH = Path(__file__).parent.parent.parent / "idl" / "pump_fun_idl.json" -_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") -_USER = Pubkey.from_string("11111111111111111111111111111112") -_BC = derive_bonding_curve(_MINT) -_ABC = derive_associated_bonding_curve(_MINT, _BC) - - -def test_buy_instructions_returns_two(): - idl = IDLParser(str(IDL_PATH)) - ixs = build_buy_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=_ABC, - creator=_USER, - is_mayhem=False, - token_amount=1000, - max_sol_cost=100_000_000, + +def test_buy_v2_args_are_two_u64s(): + built = ix.build_buy_v2_instruction( + mint=MINT, creator=CREATOR, user=USER, token_amount=123, max_quote_cost=456 ) - assert len(ixs) == 2 - - -def test_sell_instructions_returns_one(): - idl = IDLParser(str(IDL_PATH)) - ixs = build_sell_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=_ABC, - creator=_USER, - is_mayhem=False, - token_amount=1000, - min_sol_output=0, + assert bytes(built.data[8:]) == struct.pack(" list[str]: + """Derive each IDL PDA from the built accounts; return the names that disagree.""" + keys = {a["name"]: m.pubkey for a, m in zip(spec["accounts"], built.accounts, strict=False)} + bad = [] + for acc in spec["accounts"]: + pda = acc.get("pda") + if not pda or acc["name"] not in keys: + continue + seeds, resolvable = [], True + for seed in pda["seeds"]: + if seed["kind"] == "const": + seeds.append(bytes(seed["value"])) + elif seed["kind"] == "account" and seed["path"] in keys: + seeds.append(bytes(keys[seed["path"]])) + else: + resolvable = False + prog = pda.get("program") + owner = program_id + if prog and prog["kind"] == "const": + owner = Pubkey(bytes(prog["value"])) + elif prog and prog["kind"] == "account": + if prog["path"] not in keys: + resolvable = False + else: + owner = keys[prog["path"]] + if resolvable and Pubkey.find_program_address(seeds, owner)[0] != keys[acc["name"]]: + bad.append(acc["name"]) + return bad + + +@pytest.mark.parametrize("name,build,count", PUMP_BUILDERS, ids=[b[0] for b in PUMP_BUILDERS]) +def test_pump_builder_pdas_match_idl_seeds(name, build, count): + built = build() + assert ( + _resolve_pdas(_spec(PUMP_IDL, name), built, Pubkey.from_string(PUMP_IDL["address"])) == [] ) - create_ix = ixs[0] - assert create_ix.accounts[9].pubkey == MAYHEM_PROGRAM_ID - assert create_ix.accounts[9].is_writable is True - - -def test_extend_account_user_not_writable(): - """extend_account account[1] (user) must be is_writable=False, is_signer=False per IDL.""" - idl = IDLParser(str(IDL_PATH)) - ix = build_extend_account_instruction(idl=idl, bonding_curve=_BC, user=_USER) - assert ix.accounts[1].pubkey == _USER - assert ix.accounts[1].is_signer is False - assert ix.accounts[1].is_writable is False + + +@pytest.mark.parametrize( + "name,build", + [ + ( + "collect_coin_creator_fee", + lambda: ix.build_collect_coin_creator_fee_instruction(creator=USER), + ), + ("init_user_volume_accumulator", lambda: ix.build_init_amm_user_volume_accumulator(USER)), + ], +) +def test_amm_builder_pdas_match_idl_seeds(name, build): + built = build() + assert _resolve_pdas(_spec(AMM_IDL, name), built, Pubkey.from_string(AMM_IDL["address"])) == [] diff --git a/tests/test_protocol/test_pumpswap.py b/tests/test_protocol/test_pumpswap.py index 007fc36..021c0af 100644 --- a/tests/test_protocol/test_pumpswap.py +++ b/tests/test_protocol/test_pumpswap.py @@ -1,153 +1,87 @@ -"""Tests for PumpSwap pool parsing and PDA derivation.""" - -import struct +"""PumpSwap Pool and GlobalConfig parsing, fee recipients and pricing.""" +import pytest from solders.pubkey import Pubkey -from pumpfun_cli.protocol.address import ( - derive_amm_creator_vault, - derive_amm_fee_config, - derive_amm_global_volume_accumulator, - derive_amm_user_volume_accumulator, +from pumpfun_cli.protocol.contracts import USDC_MINT +from pumpfun_cli.protocol.pumpswap import ( + parse_global_config, + parse_pool_data, + pick_fee_recipients, + pool_price, +) +from tests.test_core.helpers import ( + BUYBACK_RECIPIENTS, + CREATOR, + MINT, + PROTOCOL_RECIPIENTS, + RESERVED_RECIPIENT, + build_global_config_data, + build_pool_data, ) -from pumpfun_cli.protocol.pumpswap import parse_pool_data - -_USER = Pubkey.from_string("11111111111111111111111111111112") -_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - - -def _build_pool_data() -> bytes: - """Build a synthetic pool data blob for testing parse_pool_data.""" - creator = bytes(_USER) - base_mint = bytes(_MINT) - quote_mint = bytes(Pubkey.from_string("So11111111111111111111111111111111111111112")) - lp_mint = bytes(Pubkey.from_string("11111111111111111111111111111113")) - pool_base_token = bytes(Pubkey.from_string("11111111111111111111111111111114")) - pool_quote_token = bytes(Pubkey.from_string("11111111111111111111111111111115")) - coin_creator = bytes(_USER) - - data = bytearray(243) - # discriminator (8 bytes) - data[0:8] = b"\x00" * 8 - # pool_bump (1 byte at offset 8) - data[8] = 255 - # index (u16 at offset 9) - struct.pack_into(" price = 30000 - client.get_token_account_balance.side_effect = [1_000_000, 30_000_000_000] - - pool = { - "pool_base_token_account": Pubkey.from_string("11111111111111111111111111111113"), - "pool_quote_token_account": Pubkey.from_string("11111111111111111111111111111114"), +from tests.test_core.helpers import COIN, FakeClient, add_pool + + +async def test_pool_is_derived_from_mint_and_quote(): + client = FakeClient() + pool = add_pool(client, quote_mint=USDC_MINT) + address, data = await get_pool_by_mint(client, COIN, USDC_MINT) + assert address == pool["address"] == derive_amm_pool(COIN, USDC_MINT) + with pytest.raises(RuntimeError): + await get_pool_by_mint(client, COIN) + + +async def test_effective_quote_reserves_include_virtual(): + client = FakeClient() + add_pool(client, base_reserve=1_000, quote_reserve=2_000, virtual_quote=500) + _, data = await get_pool_by_mint(client, COIN) + assert await get_pool_balances(client, parse_pool_data(data)) == (1_000, 2_500) + + +async def test_resolve_pool_tokens_reads_both_mints(): + client = FakeClient() + client.add_mint(COIN, 9, TOKEN_2022_PROGRAM) + add_pool(client, quote_mint=USDC_MINT) + _, data = await get_pool_by_mint(client, COIN, USDC_MINT) + tokens = await resolve_pool_tokens(client, parse_pool_data(data)) + assert tokens == { + "base_token_program": TOKEN_2022_PROGRAM, + "base_decimals": 9, + "quote_token_program": TOKEN_PROGRAM, + "quote_decimals": 6, } - price = await get_pool_price(client, pool) - assert price == 30_000.0 - -@pytest.mark.asyncio -async def test_get_pool_price_zero_base(): - client = AsyncMock() - client.get_token_account_balance.side_effect = [0, 30_000_000_000] - pool = { - "pool_base_token_account": Pubkey.from_string("11111111111111111111111111111113"), - "pool_quote_token_account": Pubkey.from_string("11111111111111111111111111111114"), - } - price = await get_pool_price(client, pool) - assert price == 0.0 +async def test_missing_global_config_raises(): + with pytest.raises(RuntimeError): + await get_global_config(FakeClient()) diff --git a/tests/test_protocol/test_pumpswap_instructions.py b/tests/test_protocol/test_pumpswap_instructions.py index 3985d1a..22ee42f 100644 --- a/tests/test_protocol/test_pumpswap_instructions.py +++ b/tests/test_protocol/test_pumpswap_instructions.py @@ -1,203 +1,126 @@ -"""Tests for PumpSwap instruction builders.""" +"""PumpSwap swap builders: the IDL prefix plus the accounts the program reads beyond it.""" +import pytest from solders.pubkey import Pubkey -from spl.token.instructions import get_associated_token_address +from pumpfun_cli.protocol.address import ( + derive_amm_pool_v2, + derive_amm_user_volume_accumulator, + find_ata, +) from pumpfun_cli.protocol.contracts import ( PUMP_AMM_PROGRAM, - PUMPSWAP_BUY_DISCRIMINATOR, - PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR, - PUMPSWAP_SELL_DISCRIMINATOR, - STANDARD_PUMPSWAP_FEE_RECIPIENT, + TOKEN_2022_PROGRAM, TOKEN_PROGRAM, WSOL_MINT, ) from pumpfun_cli.protocol.instructions import ( build_pumpswap_buy_exact_quote_in_instructions, - build_pumpswap_buy_instructions, build_pumpswap_sell_instructions, ) - -_USER = Pubkey.from_string("11111111111111111111111111111112") -_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") -_POOL = Pubkey.from_string("11111111111111111111111111111113") -_FEE_RECIPIENT_ATA = get_associated_token_address( - STANDARD_PUMPSWAP_FEE_RECIPIENT, - WSOL_MINT, - TOKEN_PROGRAM, -) - -_POOL_DATA = { - "base_mint": _MINT, - "quote_mint": WSOL_MINT, - "pool_base_token_account": Pubkey.from_string("11111111111111111111111111111114"), - "pool_quote_token_account": Pubkey.from_string("11111111111111111111111111111115"), - "coin_creator": _USER, -} - - -def test_pumpswap_buy_returns_five_instructions(): - ixs = build_pumpswap_buy_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - amount_out=1_000_000, - max_sol_in=100_000_000, - sol_wrap_lamports=110_000_000, +from tests.test_protocol.test_instructions import AMM_IDL, assert_matches_idl + +USER = Pubkey.new_unique() +FEE = Pubkey.new_unique() +BUYBACK = Pubkey.new_unique() +TOKENS = {"base_token_program": TOKEN_2022_PROGRAM, "quote_token_program": TOKEN_PROGRAM} + + +def _pool(*, canonical=True, cashback=False, quote=WSOL_MINT): + return { + "base_mint": Pubkey.new_unique(), + "quote_mint": quote, + "pool_base_token_account": Pubkey.new_unique(), + "pool_quote_token_account": Pubkey.new_unique(), + "coin_creator": Pubkey.new_unique() if canonical else Pubkey.default(), + "is_canonical": canonical, + "is_cashback_coin": cashback, + "is_mayhem_mode": False, + } + + +def _swap(ixs): + return next(i for i in ixs if i.program_id == PUMP_AMM_PROGRAM) + + +def _buy(pool, tokens=TOKENS, funding=1_000): + return build_pumpswap_buy_exact_quote_in_instructions( + user=USER, + pool_address=Pubkey.new_unique(), + pool=pool, + tokens=tokens, + fee_recipient=FEE, + buyback_recipient=BUYBACK, + spendable_quote_in=1_000, + min_base_amount_out=1, + wsol_funding=funding, ) - assert len(ixs) == 5 -def test_pumpswap_buy_last_ix_has_24_accounts(): - ixs = build_pumpswap_buy_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - amount_out=1_000_000, - max_sol_in=100_000_000, - sol_wrap_lamports=110_000_000, - ) - buy_ix = ixs[-1] - assert len(buy_ix.accounts) == 24 - assert buy_ix.program_id == PUMP_AMM_PROGRAM - assert buy_ix.data[:8] == PUMPSWAP_BUY_DISCRIMINATOR - - -def test_pumpswap_sell_returns_two_instructions(): - ixs = build_pumpswap_sell_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - token_amount=1_000_000, - min_sol_out=0, - ) - assert len(ixs) == 2 - - -def test_pumpswap_sell_last_ix_has_21_accounts(): - ixs = build_pumpswap_sell_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - token_amount=1_000_000, - min_sol_out=0, +def _sell(pool, tokens=TOKENS): + return build_pumpswap_sell_instructions( + user=USER, + pool_address=Pubkey.new_unique(), + pool=pool, + tokens=tokens, + fee_recipient=FEE, + buyback_recipient=BUYBACK, + token_amount=1, + min_quote_out=1, ) - sell_ix = ixs[-1] - assert len(sell_ix.accounts) == 22 - assert sell_ix.program_id == PUMP_AMM_PROGRAM - assert sell_ix.data[:8] == PUMPSWAP_SELL_DISCRIMINATOR - - -def test_pumpswap_buy_first_account_is_pool(): - ixs = build_pumpswap_buy_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - amount_out=1_000_000, - max_sol_in=100_000_000, - sol_wrap_lamports=110_000_000, - ) - buy_ix = ixs[-1] - assert buy_ix.accounts[0].pubkey == _POOL - assert buy_ix.accounts[0].is_writable is True - - -def test_pumpswap_sell_first_account_is_pool(): - ixs = build_pumpswap_sell_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - token_amount=1_000_000, - min_sol_out=0, - ) - sell_ix = ixs[-1] - assert sell_ix.accounts[0].pubkey == _POOL - assert sell_ix.accounts[0].is_writable is True - - -def test_pumpswap_buy_exact_quote_in_returns_five(): - ixs = build_pumpswap_buy_exact_quote_in_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - spendable_quote_in=100_000_000, - min_base_amount_out=1_000_000, - sol_wrap_lamports=110_000_000, - ) - assert len(ixs) == 5 -def test_pumpswap_buy_exact_quote_in_discriminator(): - ixs = build_pumpswap_buy_exact_quote_in_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - spendable_quote_in=100_000_000, - min_base_amount_out=1_000_000, - sol_wrap_lamports=110_000_000, - ) - buy_ix = ixs[-1] - assert buy_ix.data[:8] == PUMPSWAP_BUY_EXACT_QUOTE_IN_DISCRIMINATOR - assert len(buy_ix.accounts) == 24 - - -def test_pumpswap_buy_data_has_track_volume(): - """PumpSwap buy instruction data should be 25 bytes: 8 disc + 8 + 8 + 1 OptionBool.""" - ixs = build_pumpswap_buy_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - amount_out=1_000_000, - max_sol_in=100_000_000, - sol_wrap_lamports=110_000_000, - ) - buy_ix = ixs[-1] - data = bytes(buy_ix.data) - assert len(data) == 25 # 8 + 8 + 8 + 1 - assert data[-1:] == bytes([1]) - - -def test_pumpswap_buy_exact_quote_in_data_has_track_volume(): - """PumpSwap buy_exact_quote_in data should be 25 bytes with trailing OptionBool.""" - ixs = build_pumpswap_buy_exact_quote_in_instructions( - user=_USER, - pool_address=_POOL, - pool=_POOL_DATA, - token_program_id=TOKEN_PROGRAM, - fee_recipient=STANDARD_PUMPSWAP_FEE_RECIPIENT, - fee_recipient_ata=_FEE_RECIPIENT_ATA, - spendable_quote_in=100_000_000, - min_base_amount_out=1_000_000, - sol_wrap_lamports=110_000_000, - ) - buy_ix = ixs[-1] - data = bytes(buy_ix.data) - assert len(data) == 25 - assert data[-1:] == bytes([1]) +@pytest.mark.parametrize( + "canonical,cashback,buy_count,sell_count", + [(True, False, 26, 24), (True, True, 27, 26), (False, False, 25, 23), (False, True, 26, 25)], +) +def test_swap_account_counts(canonical, cashback, buy_count, sell_count): + pool = _pool(canonical=canonical, cashback=cashback) + buy, sell = _swap(_buy(pool)), _swap(_sell(pool)) + assert len(buy.accounts) == buy_count + assert len(sell.accounts) == sell_count + assert_matches_idl(AMM_IDL, "buy_exact_quote_in", buy, prefix=23) + assert_matches_idl(AMM_IDL, "sell", sell, prefix=21) + + +@pytest.mark.parametrize("canonical", [True, False]) +def test_buyback_pair_is_last_and_pool_v2_only_on_canonical(canonical): + pool = _pool(canonical=canonical) + for swap in (_swap(_buy(pool)), _swap(_sell(pool))): + keys = [m.pubkey for m in swap.accounts] + assert keys[-2] == BUYBACK + assert not swap.accounts[-2].is_writable + assert keys[-1] == find_ata(BUYBACK, WSOL_MINT, TOKEN_PROGRAM) + assert swap.accounts[-1].is_writable + assert (derive_amm_pool_v2(pool["base_mint"]) in keys) == canonical + + +def test_cashback_accounts_precede_pool_v2(): + pool = _pool(cashback=True) + acc = derive_amm_user_volume_accumulator(USER) + acc_quote = find_ata(acc, WSOL_MINT, TOKEN_PROGRAM) + buy = [m.pubkey for m in _swap(_buy(pool)).accounts] + sell = [m.pubkey for m in _swap(_sell(pool)).accounts] + assert buy[23] == acc_quote and buy[24] == derive_amm_pool_v2(pool["base_mint"]) + assert sell[21:23] == [acc_quote, acc] + + +def test_sol_buy_wraps_through_a_closed_throwaway_account(): + ixs = _buy(_pool(), funding=5_000) + swap = _swap(ixs) + user_quote = swap.accounts[6].pubkey + assert user_quote != find_ata(USER, WSOL_MINT, TOKEN_PROGRAM) + # create-with-seed, initialize, create base ATA, swap, close + assert len(ixs) == 5 + assert ixs[-1].program_id == TOKEN_PROGRAM + assert user_quote in [m.pubkey for m in ixs[-1].accounts] + + +def test_non_sol_pool_settles_through_the_wallet_ata(): + quote = Pubkey.new_unique() + tokens = {"base_token_program": TOKEN_2022_PROGRAM, "quote_token_program": TOKEN_2022_PROGRAM} + pool = _pool(quote=quote) + swap = _swap(_sell(pool, tokens)) + assert swap.accounts[6].pubkey == find_ata(USER, quote, TOKEN_2022_PROGRAM) + assert swap.accounts[12].pubkey == TOKEN_2022_PROGRAM + assert swap.accounts[-1].pubkey == find_ata(BUYBACK, quote, TOKEN_2022_PROGRAM) diff --git a/tests/test_protocol/test_quote.py b/tests/test_protocol/test_quote.py new file mode 100644 index 0000000..f60ebf9 --- /dev/null +++ b/tests/test_protocol/test_quote.py @@ -0,0 +1,132 @@ +"""Quote assets: mint resolution, the two registries, and the launch guards.""" + +import pytest +from solders.pubkey import Pubkey + +from pumpfun_cli.protocol.contracts import ( + DEFAULT_PUBKEY, + TOKEN_2022_PROGRAM, + TOKEN_PROGRAM, + USDC_MINT, + WSOL_MINT, +) +from pumpfun_cli.protocol.quote import ( + check_creator_fee, + check_mayhem_quote, + decode_global, + decode_quote_control, + get_mint_info, + normalize_quote_mint, + opening_quote_reserves, + quote_symbol, +) +from tests.test_core.helpers import ( + GLOBAL_DEFAULTS, + FakeClient, + build_global_data, + build_quote_control_data, +) + +QC_MINT = Pubkey.new_unique() + + +def test_normalize_and_symbol(): + assert normalize_quote_mint(DEFAULT_PUBKEY) == WSOL_MINT + assert normalize_quote_mint(None) == WSOL_MINT + assert quote_symbol(DEFAULT_PUBKEY) == "SOL" + assert quote_symbol(USDC_MINT) == "USDC" + assert quote_symbol(QC_MINT) == str(QC_MINT) + + +async def test_known_quote_mints_need_no_rpc(): + client = FakeClient() + assert await get_mint_info(client, WSOL_MINT) == (TOKEN_PROGRAM, 9) + assert await get_mint_info(client, USDC_MINT) == (TOKEN_PROGRAM, 6) + + +async def test_other_mints_are_read_from_chain(): + client = FakeClient() + client.add_mint(QC_MINT, 8, TOKEN_2022_PROGRAM) + assert await get_mint_info(client, QC_MINT) == (TOKEN_2022_PROGRAM, 8) + + +async def test_mint_owned_by_something_else_is_refused(): + client = FakeClient() + client.add(QC_MINT, bytes(82), owner=Pubkey.new_unique()) + with pytest.raises(RuntimeError): + await get_mint_info(client, QC_MINT) + + +async def test_missing_mint_is_refused(): + with pytest.raises(RuntimeError): + await get_mint_info(FakeClient(), QC_MINT) + + +def test_decode_global_roundtrip(): + fields = decode_global(build_global_data()) + for key in ( + "initial_virtual_token_reserves", + "initial_real_token_reserves", + "fee_basis_points", + "withdraw_authority", + "max_configurable_creator_fee_bps", + "is_holder_reward_enabled", + ): + assert fields[key] == GLOBAL_DEFAULTS[key] + assert fields["whitelisted_quote_mints"] == [USDC_MINT] + + +def test_decode_global_stops_at_truncation(): + fields = decode_global(build_global_data()[:200]) + assert "fee_basis_points" in fields + assert "is_holder_reward_enabled" not in fields + + +def test_decode_quote_control(): + assert decode_quote_control(build_quote_control_data({QC_MINT: 42})) == {QC_MINT: 42} + with pytest.raises(ValueError): + decode_quote_control(bytes(50)) + + +GLOBAL = decode_global(build_global_data()) + + +def test_opening_reserves_come_from_the_right_registry(): + registry = {QC_MINT: 7} + assert ( + opening_quote_reserves(registry, WSOL_MINT, GLOBAL) + == GLOBAL["initial_virtual_sol_reserves"] + ) + assert opening_quote_reserves(registry, QC_MINT, GLOBAL) == 7 + assert ( + opening_quote_reserves(registry, USDC_MINT, GLOBAL) + == GLOBAL["initial_virtual_quote_reserves"] + ) + with pytest.raises(ValueError): + opening_quote_reserves(registry, Pubkey.new_unique(), GLOBAL) + + +def test_creator_fee_only_on_a_quote_control_mint(): + registry = {QC_MINT: 7} + check_creator_fee(GLOBAL, registry, QC_MINT, 100) + check_creator_fee(GLOBAL, registry, WSOL_MINT, None) + check_creator_fee(GLOBAL, registry, WSOL_MINT, 0) + for mint in (WSOL_MINT, USDC_MINT): + with pytest.raises(ValueError, match="store 0"): + check_creator_fee(GLOBAL, registry, mint, 100) + + +def test_creator_fee_bounds(): + registry = {QC_MINT: 7} + with pytest.raises(ValueError, match="maximum"): + check_creator_fee(GLOBAL, registry, QC_MINT, 501) + closed = {**GLOBAL, "creator_fee_configurable": False} + with pytest.raises(ValueError, match="not accepting"): + check_creator_fee(closed, registry, QC_MINT, 100) + + +def test_mayhem_needs_sol(): + check_mayhem_quote(WSOL_MINT, True) + check_mayhem_quote(USDC_MINT, False) + with pytest.raises(ValueError): + check_mayhem_quote(USDC_MINT, True) diff --git a/tests/test_protocol/test_token_types.py b/tests/test_protocol/test_token_types.py index ab56f90..abc6823 100644 --- a/tests/test_protocol/test_token_types.py +++ b/tests/test_protocol/test_token_types.py @@ -1,262 +1,41 @@ -"""Tests for all pump.fun token type combinations. - -Covers the full matrix: Token-2022 vs SPL, mayhem vs standard, -cashback vs non-cashback, bonding curve vs PumpSwap, and instruction -account counts/fee routing for each combination. - -Ref: docs/token-types-test-matrix.md -""" - -from pathlib import Path +"""Base mints owned by either token program derive their accounts under that program.""" +import pytest from solders.pubkey import Pubkey -from pumpfun_cli.protocol.address import ( - derive_associated_bonding_curve, - derive_bonding_curve, -) -from pumpfun_cli.protocol.contracts import ( - PUMP_FEE, - PUMP_MAYHEM_FEE, - TOKEN_2022_PROGRAM, - TOKEN_PROGRAM, -) -from pumpfun_cli.protocol.idl_parser import IDLParser +from pumpfun_cli.protocol.address import derive_bonding_curve, find_ata +from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM, TOKEN_PROGRAM from pumpfun_cli.protocol.instructions import ( - build_buy_exact_sol_in_instructions, - build_buy_instructions, - build_sell_instructions, + build_buy_exact_quote_in_v2_instruction, + build_sell_v2_instruction, ) -IDL_PATH = Path(__file__).parent.parent.parent / "idl" / "pump_fun_idl.json" -_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") -_USER = Pubkey.from_string("11111111111111111111111111111112") -_CREATOR = Pubkey.from_string("11111111111111111111111111111113") -_BC = derive_bonding_curve(_MINT) - - -# BC-BUY: Bonding curve buy — all token type combinations - - -class TestBondingCurveBuyTokenTypes: - """BC-BUY-1 through BC-BUY-5: buy instruction account counts and fee routing.""" - - def _build_buy(self, *, is_mayhem: bool, token_program: Pubkey): - idl = IDLParser(str(IDL_PATH)) - abc = derive_associated_bonding_curve(_MINT, _BC, token_program) - return build_buy_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=abc, - creator=_CREATOR, - is_mayhem=is_mayhem, - token_amount=1_000_000, - max_sol_cost=100_000_000, - token_program=token_program, - ) - - def _get_fee_recipient(self, ixs): - """Fee recipient is account index 1 (second account) of the buy ix.""" - buy_ix = ixs[-1] # last ix is the buy - return buy_ix.accounts[1].pubkey - - def _get_token_program_account(self, ixs): - """Token program is account index 8 of the buy ix.""" - buy_ix = ixs[-1] - return buy_ix.accounts[8].pubkey - - def test_bc_buy_1_standard_token2022(self): - """BC-BUY-1: Standard Token-2022, no mayhem — 17 accounts, fee → PUMP_FEE.""" - ixs = self._build_buy(is_mayhem=False, token_program=TOKEN_2022_PROGRAM) - assert len(ixs) == 2 # ATA creation + buy - buy_ix = ixs[-1] - assert len(buy_ix.accounts) == 17 - assert self._get_fee_recipient(ixs) == PUMP_FEE - assert self._get_token_program_account(ixs) == TOKEN_2022_PROGRAM - - def test_bc_buy_2_mayhem_token(self): - """BC-BUY-2: Mayhem Token-2022 — 17 accounts, fee → PUMP_MAYHEM_FEE.""" - ixs = self._build_buy(is_mayhem=True, token_program=TOKEN_2022_PROGRAM) - buy_ix = ixs[-1] - assert len(buy_ix.accounts) == 17 - assert self._get_fee_recipient(ixs) == PUMP_MAYHEM_FEE - - def test_bc_buy_5_legacy_spl_token(self): - """BC-BUY-5: Legacy SPL Token — 17 accounts, token_program = TOKEN_PROGRAM.""" - ixs = self._build_buy(is_mayhem=False, token_program=TOKEN_PROGRAM) - buy_ix = ixs[-1] - assert len(buy_ix.accounts) == 17 - assert self._get_fee_recipient(ixs) == PUMP_FEE - assert self._get_token_program_account(ixs) == TOKEN_PROGRAM - - def test_bc_buy_exact_sol_in_token2022(self): - """buy_exact_sol_in also produces 17 accounts with correct fee routing.""" - idl = IDLParser(str(IDL_PATH)) - abc = derive_associated_bonding_curve(_MINT, _BC, TOKEN_2022_PROGRAM) - ixs = build_buy_exact_sol_in_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=abc, - creator=_CREATOR, - is_mayhem=False, - spendable_sol_in=10_000_000, - min_tokens_out=1_000, - token_program=TOKEN_2022_PROGRAM, - ) - buy_ix = ixs[-1] - assert len(buy_ix.accounts) == 17 - assert buy_ix.accounts[1].pubkey == PUMP_FEE - - def test_bc_buy_exact_sol_in_mayhem(self): - """buy_exact_sol_in with mayhem routes fee to PUMP_MAYHEM_FEE.""" - idl = IDLParser(str(IDL_PATH)) - abc = derive_associated_bonding_curve(_MINT, _BC, TOKEN_2022_PROGRAM) - ixs = build_buy_exact_sol_in_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=abc, - creator=_CREATOR, - is_mayhem=True, - spendable_sol_in=10_000_000, - min_tokens_out=1_000, - token_program=TOKEN_2022_PROGRAM, - ) - assert ixs[-1].accounts[1].pubkey == PUMP_MAYHEM_FEE - - -# BC-SELL: Bonding curve sell — cashback and non-cashback - - -class TestBondingCurveSellTokenTypes: - """BC-SELL-1 through BC-SELL-5: sell account counts vary by cashback flag.""" - - def _build_sell( - self, - *, - is_mayhem: bool, - is_cashback: bool, - token_program: Pubkey = TOKEN_2022_PROGRAM, +USER = Pubkey.new_unique() +MINT = Pubkey.new_unique() +CREATOR = Pubkey.new_unique() + + +@pytest.mark.parametrize("program", [TOKEN_PROGRAM, TOKEN_2022_PROGRAM]) +def test_base_accounts_follow_the_mints_program(program): + for built in ( + build_buy_exact_quote_in_v2_instruction( + mint=MINT, + creator=CREATOR, + user=USER, + spendable_quote_in=1, + min_tokens_out=1, + base_token_program=program, + ), + build_sell_v2_instruction( + mint=MINT, + creator=CREATOR, + user=USER, + token_amount=1, + min_quote_out=1, + base_token_program=program, + ), ): - idl = IDLParser(str(IDL_PATH)) - abc = derive_associated_bonding_curve(_MINT, _BC, token_program) - return build_sell_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=abc, - creator=_CREATOR, - is_mayhem=is_mayhem, - token_amount=1_000_000, - min_sol_output=0, - is_cashback=is_cashback, - token_program=token_program, - ) - - def _get_fee_recipient(self, ixs): - sell_ix = ixs[0] - return sell_ix.accounts[1].pubkey - - def test_bc_sell_1_standard_no_cashback(self): - """BC-SELL-1: Standard sell — 15 accounts (14 fixed + bonding_curve_v2).""" - ixs = self._build_sell(is_mayhem=False, is_cashback=False) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 15 - assert self._get_fee_recipient(ixs) == PUMP_FEE - - def test_bc_sell_2_mayhem_no_cashback(self): - """BC-SELL-2: Mayhem sell — 15 accounts, fee → PUMP_MAYHEM_FEE.""" - ixs = self._build_sell(is_mayhem=True, is_cashback=False) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 15 - assert self._get_fee_recipient(ixs) == PUMP_MAYHEM_FEE - - def test_bc_sell_3_cashback(self): - """BC-SELL-3: Cashback sell — 16 accounts (user_volume_accumulator added).""" - ixs = self._build_sell(is_mayhem=False, is_cashback=True) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 16 - assert self._get_fee_recipient(ixs) == PUMP_FEE - - def test_bc_sell_4_mayhem_plus_cashback(self): - """BC-SELL-4: Mayhem + cashback — 16 accounts, fee → PUMP_MAYHEM_FEE.""" - ixs = self._build_sell(is_mayhem=True, is_cashback=True) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 16 - assert self._get_fee_recipient(ixs) == PUMP_MAYHEM_FEE - - def test_bc_sell_5_legacy_spl_no_cashback(self): - """BC-SELL-5: Legacy SPL Token — 15 accounts, token_program = TOKEN_PROGRAM.""" - ixs = self._build_sell(is_mayhem=False, is_cashback=False, token_program=TOKEN_PROGRAM) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 15 - assert sell_ix.accounts[9].pubkey == TOKEN_PROGRAM - - def test_bc_sell_legacy_spl_with_cashback(self): - """Legacy SPL + cashback — 16 accounts.""" - ixs = self._build_sell(is_mayhem=False, is_cashback=True, token_program=TOKEN_PROGRAM) - sell_ix = ixs[0] - assert len(sell_ix.accounts) == 16 - assert sell_ix.accounts[9].pubkey == TOKEN_PROGRAM - - -# ATA derivation: Token-2022 vs SPL produce different addresses - - -class TestATADerivation: - """Verify ATA addresses differ between SPL and Token-2022 for same mint.""" - - def test_ata_differs_by_token_program(self): - """ATA for Token-2022 != ATA for SPL Token, even for the same mint + owner.""" - from spl.token.instructions import get_associated_token_address - - ata_2022 = get_associated_token_address(_USER, _MINT, TOKEN_2022_PROGRAM) - ata_spl = get_associated_token_address(_USER, _MINT, TOKEN_PROGRAM) - assert ata_2022 != ata_spl - - def test_associated_bonding_curve_differs_by_token_program(self): - """Associated bonding curve address differs between Token-2022 and SPL.""" - abc_2022 = derive_associated_bonding_curve(_MINT, _BC, TOKEN_2022_PROGRAM) - abc_spl = derive_associated_bonding_curve(_MINT, _BC, TOKEN_PROGRAM) - assert abc_2022 != abc_spl - - -# Sell instruction data: verify no track_volume leakage into wrong field - - -class TestSellInstructionData: - """Verify sell instruction data format is correct for all combinations.""" - - def _build_sell_data(self, *, is_cashback: bool): - idl = IDLParser(str(IDL_PATH)) - abc = derive_associated_bonding_curve(_MINT, _BC, TOKEN_2022_PROGRAM) - ixs = build_sell_instructions( - idl=idl, - mint=_MINT, - user=_USER, - bonding_curve=_BC, - assoc_bc=abc, - creator=_CREATOR, - is_mayhem=False, - token_amount=500_000, - min_sol_output=100_000, - is_cashback=is_cashback, - ) - return ixs[0].data - - def test_sell_data_length(self): - """Sell data: 8 (disc) + 8 (amount) + 8 (min_sol) = 24 bytes (no track_volume).""" - data = self._build_sell_data(is_cashback=False) - assert len(data) == 24 - - def test_sell_data_same_regardless_of_cashback(self): - """Instruction data is identical for cashback vs non-cashback (only accounts differ).""" - data_no_cb = self._build_sell_data(is_cashback=False) - data_cb = self._build_sell_data(is_cashback=True) - assert data_no_cb == data_cb + keys = [m.pubkey for m in built.accounts] + assert keys[3] == program + assert keys[11] == find_ata(derive_bonding_curve(MINT), MINT, program) + assert keys[14] == find_ata(USER, MINT, program) diff --git a/tests/test_surfpool/test_auto_routing.py b/tests/test_surfpool/test_auto_routing.py index c6b011d..002897a 100644 --- a/tests/test_surfpool/test_auto_routing.py +++ b/tests/test_surfpool/test_auto_routing.py @@ -30,7 +30,7 @@ async def test_buy_auto_route_graduated_token( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=25, ) @@ -44,7 +44,7 @@ async def test_buy_auto_route_graduated_token( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=50, rpc_timeout=SURFPOOL_TIMEOUT, ) @@ -68,7 +68,7 @@ async def test_sell_auto_route_graduated_token( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=50, rpc_timeout=SURFPOOL_TIMEOUT, confirm=True, diff --git a/tests/test_surfpool/test_graduated_info.py b/tests/test_surfpool/test_graduated_info.py index 6120b45..3605ffb 100644 --- a/tests/test_surfpool/test_graduated_info.py +++ b/tests/test_surfpool/test_graduated_info.py @@ -12,10 +12,10 @@ async def test_get_token_info_graduated(surfpool_rpc, graduated_mint): assert "error" not in result, f"Unexpected error: {result}" assert result["graduated"] is True - assert result["price_sol"] >= 0 + assert result["price"] >= 0 # Pool data is enriched only if getProgramAccounts succeeds. # On surfpool this may timeout, so pool_address is optional. if "pool_address" in result: assert result["base_reserves"] >= 0 - assert result["quote_reserves_sol"] >= 0 + assert result["quote_reserves"] >= 0 diff --git a/tests/test_surfpool/test_info.py b/tests/test_surfpool/test_info.py index fd36892..6b3f3e4 100644 --- a/tests/test_surfpool/test_info.py +++ b/tests/test_surfpool/test_info.py @@ -13,11 +13,11 @@ async def test_get_token_info(surfpool_rpc, active_mint): assert "error" not in result, f"Unexpected error: {result}" assert result["mint"] == active_mint assert result["bonding_curve"] # non-empty - assert result["price_sol"] >= 0 + assert result["price"] >= 0 assert 0 <= result["bonding_progress"] <= 100 assert isinstance(result["graduated"], bool) - assert result["real_sol_reserves"] >= 0 - assert result["virtual_sol_reserves"] > 0 + assert result["real_quote_reserves"] >= 0 + assert result["virtual_quote_reserves"] > 0 assert result["virtual_token_reserves"] > 0 diff --git a/tests/test_surfpool/test_launch.py b/tests/test_surfpool/test_launch.py index fd31f16..9be56e5 100644 --- a/tests/test_surfpool/test_launch.py +++ b/tests/test_surfpool/test_launch.py @@ -1,8 +1,8 @@ -"""Surfpool integration: token launch with mayhem/cashback flag combinations. +"""Surfpool integration: token launch across create_v2 options. -Tests launch_token() against a surfpool fork, covering the full matrix of -is_mayhem x is_cashback (with and without initial buy), verifying that -tokens are created on-chain successfully. +Tests launch_token() against a surfpool fork for mayhem and holder-reward +coins, with and without an opening buy, verifying that tokens are created +on-chain successfully. A launch with a buy goes out as a v1 transaction. The IPFS metadata upload is mocked — the on-chain program does not validate URI content, so a fake URI is sufficient for testing the @@ -16,7 +16,7 @@ from pumpfun_cli.core.info import get_token_info from pumpfun_cli.core.launch import launch_token -FAKE_URI = "https://example.com/test-metadata.json" +FAKE_URI = "https://ipfs.io/ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG" _UPLOAD_PATCH = patch( "pumpfun_cli.core.launch.upload_metadata", @@ -26,18 +26,16 @@ _LAUNCH_MATRIX = [ pytest.param(False, False, None, id="default"), - pytest.param(False, True, None, id="cashback"), + pytest.param(False, True, None, id="holder_reward"), pytest.param(True, False, None, id="mayhem"), - pytest.param(True, True, None, id="mayhem+cashback"), pytest.param(False, False, 0.001, id="default+buy"), - pytest.param(False, True, 0.001, id="cashback+buy"), + pytest.param(False, True, 0.001, id="holder_reward+buy"), pytest.param(True, False, 0.001, id="mayhem+buy"), - pytest.param(True, True, 0.001, id="mayhem+cashback+buy"), ] @pytest.mark.asyncio -@pytest.mark.parametrize("is_mayhem,is_cashback,initial_buy_sol", _LAUNCH_MATRIX) +@pytest.mark.parametrize("is_mayhem,is_holder_reward,initial_buy", _LAUNCH_MATRIX) @_UPLOAD_PATCH async def test_launch( _mock_upload, @@ -46,17 +44,17 @@ async def test_launch( test_keystore, test_password, is_mayhem, - is_cashback, - initial_buy_sol, + is_holder_reward, + initial_buy, ): - """Launch token with given mayhem/cashback/buy combination.""" + """Launch token with given mayhem/holder-reward/buy combination.""" kwargs = {} if is_mayhem: kwargs["is_mayhem"] = True - if is_cashback: - kwargs["is_cashback"] = True - if initial_buy_sol is not None: - kwargs["initial_buy_sol"] = initial_buy_sol + if is_holder_reward: + kwargs["is_holder_reward"] = True + if initial_buy is not None: + kwargs["initial_buy"] = initial_buy result = await launch_token( rpc_url=surfpool_rpc, @@ -70,11 +68,11 @@ async def test_launch( assert "error" not in result, f"Launch failed: {result}" assert result["action"] == "launch" - assert result["is_cashback"] is is_cashback + assert result["is_holder_reward"] is is_holder_reward assert result["signature"] - if initial_buy_sol is not None: - assert result["initial_buy_sol"] == initial_buy_sol + if initial_buy is not None: + assert result["initial_buy"] == initial_buy info = await get_token_info(surfpool_rpc, result["mint"]) assert "error" not in info, f"Token not found: {info}" diff --git a/tests/test_surfpool/test_pumpswap.py b/tests/test_surfpool/test_pumpswap.py index 4fe4c8f..fcf8b5d 100644 --- a/tests/test_surfpool/test_pumpswap.py +++ b/tests/test_surfpool/test_pumpswap.py @@ -20,9 +20,9 @@ async def test_get_pumpswap_info(surfpool_rpc, graduated_mint): assert "error" not in result, f"Unexpected error: {result}" assert result["pool_address"] - assert result["price_sol"] > 0 + assert result["price"] > 0 assert result["base_reserves"] >= 0 - assert result["quote_reserves_sol"] >= 0 + assert result["quote_reserves"] >= 0 assert isinstance(result["mayhem_mode"], bool) @@ -36,7 +36,7 @@ async def test_buy_pumpswap( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=25, rpc_timeout=SURFPOOL_TIMEOUT, ) @@ -45,7 +45,7 @@ async def test_buy_pumpswap( assert result["action"] == "buy" assert result["venue"] == "pumpswap" assert result["mint"] == graduated_mint - assert result["sol_spent"] == 0.001 + assert result["quote_spent"] == 0.001 assert result["tokens_received"] > 0 assert result["signature"] @@ -61,7 +61,7 @@ async def test_buy_then_sell_pumpswap( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=50, rpc_timeout=SURFPOOL_TIMEOUT, confirm=True, @@ -82,7 +82,7 @@ async def test_buy_then_sell_pumpswap( assert sell_result["action"] == "sell" assert sell_result["venue"] == "pumpswap" assert sell_result["tokens_sold"] > 0 - assert sell_result["sol_received"] > 0 + assert sell_result["quote_received"] > 0 assert sell_result["signature"] @@ -96,7 +96,7 @@ async def test_buy_then_partial_sell_pumpswap( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=50, rpc_timeout=SURFPOOL_TIMEOUT, confirm=True, diff --git a/tests/test_surfpool/test_token_types.py b/tests/test_surfpool/test_token_types.py index 9adf5d1..0b752a1 100644 --- a/tests/test_surfpool/test_token_types.py +++ b/tests/test_surfpool/test_token_types.py @@ -31,7 +31,7 @@ async def test_buy_cashback_token( keystore_path=str(test_keystore), password=test_password, mint_str=cashback_mint, - sol_amount=0.001, + amount=0.001, slippage=25, ) @@ -51,7 +51,7 @@ async def test_buy_then_sell_cashback_token( keystore_path=str(test_keystore), password=test_password, mint_str=cashback_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=True, compute_units=200_000, @@ -70,7 +70,7 @@ async def test_buy_then_sell_cashback_token( assert "error" not in sell_result, f"Sell cashback token failed: {sell_result}" assert sell_result["action"] == "sell" assert sell_result["tokens_sold"] > 0 - assert sell_result["sol_received"] > 0 + assert sell_result["quote_received"] > 0 # --------------------------------------------------------------------------- @@ -88,7 +88,7 @@ async def test_cashback_token_dry_run( keystore_path=str(test_keystore), password=test_password, mint_str=cashback_mint, - sol_amount=0.001, + amount=0.001, slippage=25, dry_run=True, ) diff --git a/tests/test_surfpool/test_trade.py b/tests/test_surfpool/test_trade.py index 73d4cd7..ddbc20f 100644 --- a/tests/test_surfpool/test_trade.py +++ b/tests/test_surfpool/test_trade.py @@ -13,14 +13,14 @@ async def test_buy_token(surfpool_rpc, funded_keypair, test_keystore, test_passw keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, ) assert "error" not in result, f"Buy failed: {result}" assert result["action"] == "buy" assert result["mint"] == active_mint - assert result["sol_spent"] == 0.001 + assert result["quote_spent"] == 0.001 assert result["tokens_received"] > 0 assert result["signature"] @@ -36,7 +36,7 @@ async def test_buy_then_sell( keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=True, ) @@ -54,7 +54,7 @@ async def test_buy_then_sell( assert "error" not in sell_result, f"Sell failed: {sell_result}" assert sell_result["action"] == "sell" assert sell_result["tokens_sold"] > 0 - assert sell_result["sol_received"] > 0 + assert sell_result["quote_received"] > 0 assert sell_result["signature"] @@ -68,7 +68,7 @@ async def test_buy_with_confirm( keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=True, ) @@ -87,7 +87,7 @@ async def test_buy_graduated_token_fails( keystore_path=str(test_keystore), password=test_password, mint_str=graduated_mint, - sol_amount=0.001, + amount=0.001, slippage=25, ) assert result["error"] == "graduated" @@ -103,7 +103,7 @@ async def test_buy_then_partial_sell( keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=True, ) @@ -135,7 +135,7 @@ async def test_buy_without_confirm( keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=False, ) diff --git a/tests/test_surfpool/test_wallet_transfer.py b/tests/test_surfpool/test_wallet_transfer.py index 2c1694c..943eeef 100644 --- a/tests/test_surfpool/test_wallet_transfer.py +++ b/tests/test_surfpool/test_wallet_transfer.py @@ -61,7 +61,7 @@ async def test_buy_then_transfer_token( keystore_path=str(test_keystore), password=test_password, mint_str=active_mint, - sol_amount=0.001, + amount=0.001, slippage=25, confirm=True, ) diff --git a/uv.lock b/uv.lock index 084a420..3bb42ff 100644 --- a/uv.lock +++ b/uv.lock @@ -1,13 +1,36 @@ version = 1 +revision = 5 requires-python = ">=3.12" +[options] +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer-span = "P7D" + +[[package]] +name = "aiolimiter" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/60/0d16f90083a2f0ae9421d11ad98287f7942414f091ae9ad318389a764f85/aiolimiter-1.3.0.tar.gz", hash = "sha256:7343008c2228e89def7d4ce29ab98ee98822bf5db69018c09c90088929f7c104", size = 10051, upload-time = "2026-09-07T14:40:27.876Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/d8/9237b1d29e561bd37ffe9487ea1a4551d2df2902d9b79a6ea6b18e4fcc73/aiolimiter-1.3.0-py3-none-any.whl", hash = "sha256:c0c16c377049fb2e40cc3373770e29c063de32aa25d84e5db168c854da6462b7", size = 6955, upload-time = "2026-09-07T14:40:26.753Z" }, +] + [[package]] name = "annotated-doc" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, ] [[package]] @@ -18,27 +41,40 @@ dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] [[package]] name = "base58" version = "2.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c", size = 6528 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c", size = 6528, upload-time = "2021-10-30T22:12:17.858Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2", size = 5621 }, + { url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2", size = 5621, upload-time = "2021-10-30T22:12:16.658Z" }, ] [[package]] name = "certifi" version = "2026.10.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598, upload-time = "2026-10-20T02:24:16.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464 }, + { url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464, upload-time = "2026-10-20T02:24:15.072Z" }, ] [[package]] @@ -48,63 +84,63 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271 }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048 }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529 }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097 }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983 }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519 }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572 }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963 }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361 }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] [[package]] name = "cfgv" version = "3.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] [[package]] @@ -114,124 +150,125 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "construct" version = "2.10.70" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/77/8c84b98eca70d245a2a956452f21d57930d22ab88cbeed9290ca630cf03f/construct-2.10.70.tar.gz", hash = "sha256:4d2472f9684731e58cc9c56c463be63baa1447d674e0d66aeb5627b22f512c29", size = 86337 } +sdist = { url = "https://files.pythonhosted.org/packages/02/77/8c84b98eca70d245a2a956452f21d57930d22ab88cbeed9290ca630cf03f/construct-2.10.70.tar.gz", hash = "sha256:4d2472f9684731e58cc9c56c463be63baa1447d674e0d66aeb5627b22f512c29", size = 86337, upload-time = "2023-11-29T08:44:49.545Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/fb/08b3f4bf05da99aba8ffea52a558758def16e8516bc75ca94ff73587e7d3/construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30", size = 63020 }, + { url = "https://files.pythonhosted.org/packages/b2/fb/08b3f4bf05da99aba8ffea52a558758def16e8516bc75ca94ff73587e7d3/construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30", size = 63020, upload-time = "2023-11-29T08:44:46.876Z" }, ] [[package]] name = "construct-typing" -version = "0.7.0" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "arrow" }, { name = "construct" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/ae/659fe4866d89ef5a3a65cddbdd7b35882f4feb72db383821965f2fcea934/construct_typing-0.7.0.tar.gz", hash = "sha256:71d110dedff39bd3b603c734077032a7065bc597a49db1f5b03a211d05dbac23", size = 45104 } +sdist = { url = "https://files.pythonhosted.org/packages/61/de/de378332a7cb2f62df134e0b5915a0fea939219c80ecee99b29da8ba49c7/construct_typing-0.8.1.tar.gz", hash = "sha256:b3c33246b83f187f21fbfa9e229cf7225fe42823f3d598328b973c844071c456", size = 53118, upload-time = "2026-07-23T08:48:19.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/0c/2db6f7e1ae9795e436c6a0dc0bc38b12b8c8a228cb63203e24190b755b3b/construct_typing-0.7.0-py3-none-any.whl", hash = "sha256:c92383c6e8e5d07ba25811c8d5163820458d821e73bb1006541f43f89788646c", size = 24350 }, + { url = "https://files.pythonhosted.org/packages/95/f4/7dd59e010765f5c0043a5cf59e0f3e6748cd966491645bbb10b88ad24c82/construct_typing-0.8.1-py3-none-any.whl", hash = "sha256:9b5845dbaf959c9d960f5105f9ab3533227c4da2b76baa57d1f1c0aa2a088cdb", size = 26727, upload-time = "2026-07-23T08:48:17.908Z" }, ] [[package]] name = "coverage" version = "7.13.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449 }, - { url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810 }, - { url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308 }, - { url = "https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", size = 254052 }, - { url = "https://files.pythonhosted.org/packages/e8/ac/45dc2e19a1939098d783c846e130b8f862fbb50d09e0af663988f2f21973/coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", size = 255165 }, - { url = "https://files.pythonhosted.org/packages/2d/4d/26d236ff35abc3b5e63540d3386e4c3b192168c1d96da5cb2f43c640970f/coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", size = 257432 }, - { url = "https://files.pythonhosted.org/packages/ec/55/14a966c757d1348b2e19caf699415a2a4c4f7feaa4bbc6326a51f5c7dd1b/coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", size = 251716 }, - { url = "https://files.pythonhosted.org/packages/77/33/50116647905837c66d28b2af1321b845d5f5d19be9655cb84d4a0ea806b4/coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", size = 253089 }, - { url = "https://files.pythonhosted.org/packages/c2/b4/8efb11a46e3665d92635a56e4f2d4529de6d33f2cb38afd47d779d15fc99/coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", size = 251232 }, - { url = "https://files.pythonhosted.org/packages/51/24/8cd73dd399b812cc76bb0ac260e671c4163093441847ffe058ac9fda1e32/coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", size = 255299 }, - { url = "https://files.pythonhosted.org/packages/03/94/0a4b12f1d0e029ce1ccc1c800944a9984cbe7d678e470bb6d3c6bc38a0da/coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", size = 250796 }, - { url = "https://files.pythonhosted.org/packages/73/44/6002fbf88f6698ca034360ce474c406be6d5a985b3fdb3401128031eef6b/coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0", size = 252673 }, - { url = "https://files.pythonhosted.org/packages/de/c6/a0279f7c00e786be75a749a5674e6fa267bcbd8209cd10c9a450c655dfa7/coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246", size = 221990 }, - { url = "https://files.pythonhosted.org/packages/77/4e/c0a25a425fcf5557d9abd18419c95b63922e897bc86c1f327f155ef234a9/coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126", size = 222800 }, - { url = "https://files.pythonhosted.org/packages/47/ac/92da44ad9a6f4e3a7debd178949d6f3769bedca33830ce9b1dcdab589a37/coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d", size = 221415 }, - { url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474 }, - { url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844 }, - { url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832 }, - { url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434 }, - { url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676 }, - { url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807 }, - { url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058 }, - { url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805 }, - { url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766 }, - { url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923 }, - { url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591 }, - { url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364 }, - { url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010 }, - { url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818 }, - { url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438 }, - { url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165 }, - { url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516 }, - { url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804 }, - { url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885 }, - { url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308 }, - { url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452 }, - { url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057 }, - { url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875 }, - { url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500 }, - { url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212 }, - { url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398 }, - { url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584 }, - { url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688 }, - { url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746 }, - { url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003 }, - { url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522 }, - { url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855 }, - { url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887 }, - { url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396 }, - { url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745 }, - { url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055 }, - { url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911 }, - { url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754 }, - { url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720 }, - { url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994 }, - { url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531 }, - { url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189 }, - { url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258 }, - { url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073 }, - { url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638 }, - { url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246 }, - { url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514 }, - { url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877 }, - { url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004 }, - { url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408 }, - { url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544 }, - { url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980 }, - { url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871 }, - { url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472 }, - { url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210 }, - { url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319 }, - { url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638 }, - { url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040 }, - { url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148 }, - { url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172 }, - { url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242 }, +sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" }, + { url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" }, + { url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", size = 254052, upload-time = "2026-02-09T12:56:59.754Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ac/45dc2e19a1939098d783c846e130b8f862fbb50d09e0af663988f2f21973/coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", size = 255165, upload-time = "2026-02-09T12:57:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4d/26d236ff35abc3b5e63540d3386e4c3b192168c1d96da5cb2f43c640970f/coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", size = 257432, upload-time = "2026-02-09T12:57:02.637Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/14a966c757d1348b2e19caf699415a2a4c4f7feaa4bbc6326a51f5c7dd1b/coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", size = 251716, upload-time = "2026-02-09T12:57:04.056Z" }, + { url = "https://files.pythonhosted.org/packages/77/33/50116647905837c66d28b2af1321b845d5f5d19be9655cb84d4a0ea806b4/coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", size = 253089, upload-time = "2026-02-09T12:57:05.503Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/8efb11a46e3665d92635a56e4f2d4529de6d33f2cb38afd47d779d15fc99/coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", size = 251232, upload-time = "2026-02-09T12:57:06.879Z" }, + { url = "https://files.pythonhosted.org/packages/51/24/8cd73dd399b812cc76bb0ac260e671c4163093441847ffe058ac9fda1e32/coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", size = 255299, upload-time = "2026-02-09T12:57:08.245Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/0a4b12f1d0e029ce1ccc1c800944a9984cbe7d678e470bb6d3c6bc38a0da/coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", size = 250796, upload-time = "2026-02-09T12:57:10.142Z" }, + { url = "https://files.pythonhosted.org/packages/73/44/6002fbf88f6698ca034360ce474c406be6d5a985b3fdb3401128031eef6b/coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0", size = 252673, upload-time = "2026-02-09T12:57:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/de/c6/a0279f7c00e786be75a749a5674e6fa267bcbd8209cd10c9a450c655dfa7/coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246", size = 221990, upload-time = "2026-02-09T12:57:14.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/4e/c0a25a425fcf5557d9abd18419c95b63922e897bc86c1f327f155ef234a9/coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126", size = 222800, upload-time = "2026-02-09T12:57:15.944Z" }, + { url = "https://files.pythonhosted.org/packages/47/ac/92da44ad9a6f4e3a7debd178949d6f3769bedca33830ce9b1dcdab589a37/coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d", size = 221415, upload-time = "2026-02-09T12:57:17.497Z" }, + { url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" }, + { url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" }, + { url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" }, + { url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" }, + { url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" }, + { url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" }, + { url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010, upload-time = "2026-02-09T12:57:37.25Z" }, + { url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818, upload-time = "2026-02-09T12:57:38.734Z" }, + { url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438, upload-time = "2026-02-09T12:57:40.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" }, + { url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" }, + { url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" }, + { url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" }, + { url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688, upload-time = "2026-02-09T12:58:02.736Z" }, + { url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746, upload-time = "2026-02-09T12:58:05.362Z" }, + { url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003, upload-time = "2026-02-09T12:58:06.952Z" }, + { url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" }, + { url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" }, + { url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" }, + { url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" }, + { url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" }, + { url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" }, + { url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258, upload-time = "2026-02-09T12:58:29.441Z" }, + { url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073, upload-time = "2026-02-09T12:58:31.026Z" }, + { url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638, upload-time = "2026-02-09T12:58:32.599Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" }, + { url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" }, + { url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" }, + { url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" }, + { url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" }, + { url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" }, + { url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" }, + { url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" }, + { url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040, upload-time = "2026-02-09T12:58:56.936Z" }, + { url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148, upload-time = "2026-02-09T12:58:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172, upload-time = "2026-02-09T12:59:00.396Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" }, ] [[package]] @@ -241,77 +278,99 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289 }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637 }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742 }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528 }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993 }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855 }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635 }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038 }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181 }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482 }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497 }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819 }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230 }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909 }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287 }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728 }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287 }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291 }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539 }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199 }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131 }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072 }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170 }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741 }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728 }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001 }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637 }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487 }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514 }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349 }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667 }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980 }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143 }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674 }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801 }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755 }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539 }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794 }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160 }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123 }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220 }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050 }, +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, + { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "filelock" version = "3.25.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8b/4c32ecde6bea6486a2a5d05340e695174351ff6b06cf651a74c005f9df00/filelock-3.25.1.tar.gz", hash = "sha256:b9a2e977f794ef94d77cdf7d27129ac648a61f585bff3ca24630c1629f701aa9", size = 40319 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8b/4c32ecde6bea6486a2a5d05340e695174351ff6b06cf651a74c005f9df00/filelock-3.25.1.tar.gz", hash = "sha256:b9a2e977f794ef94d77cdf7d27129ac648a61f585bff3ca24630c1629f701aa9", size = 40319, upload-time = "2026-03-09T19:38:47.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl", hash = "sha256:18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf", size = 26720 }, + { url = "https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl", hash = "sha256:18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf", size = 26720, upload-time = "2026-03-09T19:38:45.718Z" }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h2" +version = "4.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/85/7c366e69d84c17bb778fe41419e1fbcce3033d5b7ce29bbffff0a98b859f/h2-4.4.1.tar.gz", hash = "sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516", size = 2157281, upload-time = "2026-08-03T11:45:09.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/22/e85faf23bd72a92d1921e37d674ca56eb298a3c8be31fdecef0ff2b3aaac/h2-4.4.1-py3-none-any.whl", hash = "sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6", size = 62636, upload-time = "2026-08-03T11:44:59.164Z" }, +] + +[[package]] +name = "hpack" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, + { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" }, ] [[package]] @@ -322,9 +381,22 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpcore2" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "truststore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/61/afa9d6b7452f9eca73f73d45324eb0bb52f098fd5e6049bf6b8263be9b4e/httpcore2-2.16.0.tar.gz", hash = "sha256:c72cd0413563b17e1cd8354677dff887466f8ec2551679759e93126534428643", size = 69886, upload-time = "2026-10-21T07:09:45.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/c2/ce417c397afa5181f0e3c6ff01a4d01639414e1adef08730d97571297151/httpcore2-2.16.0-py3-none-any.whl", hash = "sha256:5c4358319f2602d984ad93182c9fee5160f22e9da7babdda870a5e7c4e2dc34f", size = 85070, upload-time = "2026-10-21T07:09:42.802Z" }, ] [[package]] @@ -337,45 +409,85 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx2" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform != 'emscripten'" }, + { name = "httpcore2", marker = "sys_platform != 'emscripten'" }, + { name = "httpx2-jsfetch", marker = "sys_platform == 'emscripten'" }, + { name = "idna" }, + { name = "truststore", marker = "sys_platform != 'emscripten'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/73/5ca2cedf5e4e50423d1c258877865a6b62793633b561c58499f8aeb33b7e/httpx2-2.16.0.tar.gz", hash = "sha256:0d54c98128e8f2ff7fbbde0fca8c359e5f032edeaaaac443cdb7f31f685bcd7f", size = 103116, upload-time = "2026-10-21T07:09:46.348Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/3d/2e4bc4fd1ef9770871669adb348d612a6e4786b303a23f9935b81c48dd60/httpx2-2.16.0-py3-none-any.whl", hash = "sha256:fd2c5d7909e8d86fe92d53020c310ca0e4e431b838c207c5754b9bbf0308d49a", size = 97880, upload-time = "2026-10-21T07:09:44.01Z" }, +] + +[package.optional-dependencies] +http2 = [ + { name = "h2" }, +] + +[[package]] +name = "httpx2-jsfetch" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] [[package]] name = "identify" version = "2.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/84/376a3b96e5a8d33a7aa2c5b3b31a4b3c364117184bf0b17418055f6ace66/identify-2.6.17.tar.gz", hash = "sha256:f816b0b596b204c9fdf076ded172322f2723cf958d02f9c3587504834c8ff04d", size = 99579 } +sdist = { url = "https://files.pythonhosted.org/packages/57/84/376a3b96e5a8d33a7aa2c5b3b31a4b3c364117184bf0b17418055f6ace66/identify-2.6.17.tar.gz", hash = "sha256:f816b0b596b204c9fdf076ded172322f2723cf958d02f9c3587504834c8ff04d", size = 99579, upload-time = "2026-03-01T20:04:12.702Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl", hash = "sha256:be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0", size = 99382 }, + { url = "https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl", hash = "sha256:be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0", size = 99382, upload-time = "2026-03-01T20:04:11.439Z" }, ] [[package]] name = "idna" -version = "3.11" +version = "3.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, + { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, ] [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] name = "jsonalias" version = "0.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/45/ee7e17002cb7f3264f755ff6a1a72c55d1830e07808d643167d2a2277c4f/jsonalias-0.1.1.tar.gz", hash = "sha256:64f04d935397d579fc94509e1fcb6212f2d081235d9d6395bd10baedf760a769", size = 1095 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/45/ee7e17002cb7f3264f755ff6a1a72c55d1830e07808d643167d2a2277c4f/jsonalias-0.1.1.tar.gz", hash = "sha256:64f04d935397d579fc94509e1fcb6212f2d081235d9d6395bd10baedf760a769", size = 1095, upload-time = "2022-10-28T22:57:56.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/ed/05aebce69f78c104feff2ffcdd5a6f9d668a208aba3a8bf56e3750809fd8/jsonalias-0.1.1-py3-none-any.whl", hash = "sha256:a56d2888e6397812c606156504e861e8ec00e188005af149f003c787db3d3f18", size = 1312 }, + { url = "https://files.pythonhosted.org/packages/41/ed/05aebce69f78c104feff2ffcdd5a6f9d668a208aba3a8bf56e3750809fd8/jsonalias-0.1.1-py3-none-any.whl", hash = "sha256:a56d2888e6397812c606156504e861e8ec00e188005af149f003c787db3d3f18", size = 1312, upload-time = "2022-10-28T22:57:54.763Z" }, ] [[package]] @@ -385,54 +497,54 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "nodeenv" version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] [[package]] name = "packaging" version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416 } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366 }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] name = "platformdirs" version = "4.9.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737 } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216 }, + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] @@ -446,14 +558,14 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232 } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437 }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] [[package]] name = "pumpfun-cli" -version = "0.1.0" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "base58" }, @@ -461,6 +573,7 @@ dependencies = [ { name = "httpx" }, { name = "python-dotenv" }, { name = "solana" }, + { name = "solders" }, { name = "tomli-w" }, { name = "typer" }, ] @@ -481,7 +594,8 @@ requires-dist = [ { name = "cryptography" }, { name = "httpx" }, { name = "python-dotenv" }, - { name = "solana" }, + { name = "solana", specifier = ">=0.40.3" }, + { name = "solders", specifier = ">=0.29.0" }, { name = "tomli-w" }, { name = "typer" }, ] @@ -500,18 +614,139 @@ dev = [ name = "pycparser" version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/0b/8e10b2e693af8ec54346a14caf36221334775975a747c9ceaa3f8371d96d/pydantic-2.14.1.tar.gz", hash = "sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26", size = 900362, upload-time = "2026-10-11T18:37:55.396Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, + { url = "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl", hash = "sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454", size = 488106, upload-time = "2026-10-11T18:37:53.437Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.50.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/24/af4ec4be49fbc810f35b0bdcacc3d433b56bbfa469fd3bfd4ae116cd9bf1/pydantic_core-2.50.1.tar.gz", hash = "sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09", size = 513515, upload-time = "2026-10-11T18:35:44.82Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/94/101b49a6c63f99586755690696eae14532772587d9e39e93f7b8d28a49da/pydantic_core-2.50.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0", size = 2222428, upload-time = "2026-10-11T18:32:20.228Z" }, + { url = "https://files.pythonhosted.org/packages/db/a3/f8b1b39349c33037c73d7a1b794c9c29d8f07d5bd2b74399c1375a529cc6/pydantic_core-2.50.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e", size = 2025779, upload-time = "2026-10-11T18:32:21.723Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2d/862a7d115305cc621358e51ba13f296e0ffdf4fbd984be9f63bc7862730b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41", size = 2048429, upload-time = "2026-10-11T18:32:23.3Z" }, + { url = "https://files.pythonhosted.org/packages/c0/7d/fed95d18bf2b5abe2d5ab2bbec4184bfc22b6805994a33ccdae8edffd25a/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3", size = 2173274, upload-time = "2026-10-11T18:32:24.697Z" }, + { url = "https://files.pythonhosted.org/packages/93/1e/c6f22ceac65dc3d291ff79bea0eb74cbd1de8b128f01fee48af03bcea1e1/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5", size = 2360403, upload-time = "2026-10-11T18:32:26.281Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/190b5b6bfe4c72f265feb657913eb30749193927a5ad7b83cfa7866a25a2/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6", size = 2439799, upload-time = "2026-10-11T18:32:27.844Z" }, + { url = "https://files.pythonhosted.org/packages/90/24/48cd98388e4ac2c08d0800af36d15413db15e20387f1038123a2c86cb2f6/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e", size = 2160157, upload-time = "2026-10-11T18:32:29.553Z" }, + { url = "https://files.pythonhosted.org/packages/00/86/d909133d0b9979ed14c20f110f21124a0e346caf071bd66ac5af3009062b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a", size = 2229523, upload-time = "2026-10-11T18:32:31.027Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/4b43f08131b2098e73a852fd932063385f3103b33839fd9d1abdb89e673d/pydantic_core-2.50.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9", size = 2242700, upload-time = "2026-10-11T18:32:32.622Z" }, + { url = "https://files.pythonhosted.org/packages/88/77/de39658569dc7a4c293d110a89629f61145e7538f00d1e4280c28debefd3/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff", size = 2278452, upload-time = "2026-10-11T18:32:34.165Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/1eeb9e4e791e81210ef9096f58080b32ba15b5b13c108d50aaa3f8ce9f73/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4", size = 2450098, upload-time = "2026-10-11T18:32:35.547Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/b2accfdff17e5a88d6358536702146de00f9339adaa4d5bbe1ee93cb4832/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2", size = 2476839, upload-time = "2026-10-11T18:32:37.119Z" }, + { url = "https://files.pythonhosted.org/packages/3b/7e/c8a83eb5d2cd42c2e13507150fb777b1edea5a018adc9cbf2858a1d08285/pydantic_core-2.50.1-cp312-cp312-win32.whl", hash = "sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e", size = 2101425, upload-time = "2026-10-11T18:32:38.593Z" }, + { url = "https://files.pythonhosted.org/packages/c5/17/8bbd530b8659e9d963f5b16f6cb8e159f4cd174b4d6304e48969ab12dba8/pydantic_core-2.50.1-cp312-cp312-win_amd64.whl", hash = "sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498", size = 2172522, upload-time = "2026-10-11T18:32:40.112Z" }, + { url = "https://files.pythonhosted.org/packages/af/9f/a79d690b127e4e3cff143d031538ab9aa715f6b2fbeecbd07cd6f2bf9fb7/pydantic_core-2.50.1-cp312-cp312-win_arm64.whl", hash = "sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1", size = 2161366, upload-time = "2026-10-11T18:32:41.606Z" }, + { url = "https://files.pythonhosted.org/packages/ae/57/0e237d7091d2cd44a35d243b7344227f90440166860469cd036afc23a04d/pydantic_core-2.50.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b", size = 2222774, upload-time = "2026-10-11T18:32:43.103Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b9/c720e56858d4e1539503297ed37063e0c08e0f3541c41b777f6a800f75fa/pydantic_core-2.50.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482", size = 2024738, upload-time = "2026-10-11T18:32:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b8/fbfc25875219cc060e613170ff10e850c6d8924beb4910da57c2ee3ba1d2/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d", size = 2047049, upload-time = "2026-10-11T18:32:46.164Z" }, + { url = "https://files.pythonhosted.org/packages/d7/43/34210124d504c553688f2f04b48500b131237528ac545b445e0d6d30e0d5/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658", size = 2173415, upload-time = "2026-10-11T18:32:47.722Z" }, + { url = "https://files.pythonhosted.org/packages/65/cf/6e178e8fdc11da5965bef980983bf46326a42f436871dd51ba0a57f39df1/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4", size = 2361719, upload-time = "2026-10-11T18:32:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/11/14/bd5169d356aa91bf777e28ba0b281c192d286d03c2812c9c9db023a2f00e/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255", size = 2439803, upload-time = "2026-10-11T18:32:51.025Z" }, + { url = "https://files.pythonhosted.org/packages/1c/bc/d79d000e5203ebef39af839f2ce77a777fcad6e08ad26af9a2fcd114ffc8/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec", size = 2158402, upload-time = "2026-10-11T18:32:52.714Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/4902cb5fd599422c130bd3124ab31ee8b771199bd56662702eed07d3fec7/pydantic_core-2.50.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72", size = 2229755, upload-time = "2026-10-11T18:32:54.126Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f5/c1481f8669f6060d89110c9b1374173fc8767ca276b84f4870bd8acd5e3f/pydantic_core-2.50.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c", size = 2242303, upload-time = "2026-10-11T18:32:55.641Z" }, + { url = "https://files.pythonhosted.org/packages/04/f9/77fc3c7653ba9b6e42049e17e96b25388187d4a7c274ab1cb07f58ac8419/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568", size = 2278345, upload-time = "2026-10-11T18:32:57.38Z" }, + { url = "https://files.pythonhosted.org/packages/95/9b/0579c5d12e7f2b16b27e6782427987341fcce07b0725e02ddb0b74add0c4/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8", size = 2449947, upload-time = "2026-10-11T18:32:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ac/1b677db91eba54cc5922f4de6edc46ba45c2bd712dfdc0130382d158e214/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4", size = 2476889, upload-time = "2026-10-11T18:33:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/4d/2a/3a9f6624ee3ea9ccba5249dde11418bb4c35780a7a92608f8768bd3fea39/pydantic_core-2.50.1-cp313-cp313-win32.whl", hash = "sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685", size = 2101118, upload-time = "2026-10-11T18:33:02.329Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1f/323f78ddd9d9938aac420c1abb4e8ba799fc8bdab0acc67c0593b837c979/pydantic_core-2.50.1-cp313-cp313-win_amd64.whl", hash = "sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f", size = 2170840, upload-time = "2026-10-11T18:33:03.919Z" }, + { url = "https://files.pythonhosted.org/packages/cb/09/8497c52a739ae425c3ac2f7f56414cbc711c67d374346174f40fe2062644/pydantic_core-2.50.1-cp313-cp313-win_arm64.whl", hash = "sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662", size = 2161633, upload-time = "2026-10-11T18:33:05.518Z" }, + { url = "https://files.pythonhosted.org/packages/49/33/28b96e81677153715e3eafb9f26663a80841e859fde282a380359b0d3fa1/pydantic_core-2.50.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a", size = 2224428, upload-time = "2026-10-11T18:33:07.153Z" }, + { url = "https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9", size = 2023740, upload-time = "2026-10-11T18:33:08.763Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c", size = 2049435, upload-time = "2026-10-11T18:33:10.366Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8e/f195aebec49ad12318876ac2368c197a7939f5d52f8e156d2238ef8a4588/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb", size = 2172975, upload-time = "2026-10-11T18:33:12.368Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f5/7ad9fb83010cd5ea0948409db105676ed779c4e709e2d3d89b9fe2558794/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597", size = 2364197, upload-time = "2026-10-11T18:33:14.244Z" }, + { url = "https://files.pythonhosted.org/packages/ca/fb/bf0aab3e78301d202b82a0322ec968cd703b11fc0623fde9a28708936f62/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899", size = 2444259, upload-time = "2026-10-11T18:33:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea", size = 2159857, upload-time = "2026-10-11T18:33:17.51Z" }, + { url = "https://files.pythonhosted.org/packages/65/a0/fb0a3ca10f139dcf765b2d312d10cf0f65c57c59993229d00ad12b14ceff/pydantic_core-2.50.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e", size = 2233954, upload-time = "2026-10-11T18:33:19.591Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6a/e63842252702aa4ec6e6b7178ca85e541a77459076592c23ebe2d9840b33/pydantic_core-2.50.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20", size = 2243876, upload-time = "2026-10-11T18:33:21.393Z" }, + { url = "https://files.pythonhosted.org/packages/39/25/5991cf8318b37e0dfab47b87541619a1df8501a793cba0d978846cba37a7/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc", size = 2277908, upload-time = "2026-10-11T18:33:23.324Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3e/3ee8baaa6cc25a6961c69168cf9ff0f002d56f4e0d541ad6724c18fb61f3/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396", size = 2449285, upload-time = "2026-10-11T18:33:24.942Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/acbec6deac13fe697a80c275a9b6661db62c4d323bac8a47347b1f39c7cd/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb", size = 2479013, upload-time = "2026-10-11T18:33:26.925Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/21a3f237b264389f6219d053ee78fc1b5c2fd402c1b1cb2b6cb8b85f9834/pydantic_core-2.50.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5", size = 1484400, upload-time = "2026-10-11T18:33:28.693Z" }, + { url = "https://files.pythonhosted.org/packages/09/ce/077a6d262d12ef09108377ac0717f029f420d773ace63cafd6143af75568/pydantic_core-2.50.1-cp314-cp314-win32.whl", hash = "sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2", size = 2103222, upload-time = "2026-10-11T18:33:30.326Z" }, + { url = "https://files.pythonhosted.org/packages/14/4c/350a2415209c43d670eb71d3c040f332c04a30583d39e311b05c7ac15762/pydantic_core-2.50.1-cp314-cp314-win_amd64.whl", hash = "sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f", size = 2171586, upload-time = "2026-10-11T18:33:32.139Z" }, + { url = "https://files.pythonhosted.org/packages/bf/92/9bea6ca96580a0902fed366f064f0829e404f41889b34543279a1162b888/pydantic_core-2.50.1-cp314-cp314-win_arm64.whl", hash = "sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea", size = 2155773, upload-time = "2026-10-11T18:33:33.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/8c/f121f073cf32bdd5cba7e6230b1ce0ac845b0cf43e44dd597d95af272db2/pydantic_core-2.50.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070", size = 2234017, upload-time = "2026-10-11T18:33:35.588Z" }, + { url = "https://files.pythonhosted.org/packages/a4/69/2bb2bcd6146cffe98d760a46c42ae71efd5151d9b2f9c9bf6619a3b32083/pydantic_core-2.50.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e", size = 2001016, upload-time = "2026-10-11T18:33:37.57Z" }, + { url = "https://files.pythonhosted.org/packages/20/b3/fbf854c7d07ec114260c26e9e2071a4381740f9ae09641dbfcbdf2a18c45/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5", size = 2023015, upload-time = "2026-10-11T18:33:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/65/20/6de55b2f92cdb614b745c6e9ced639fc4fd7e1e77604825a88bdece6fcbd/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb", size = 2192543, upload-time = "2026-10-11T18:33:41.381Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d9/19e91c94bd5c405945ce2f15526808aea37e162c253160de8ed7bf70b406/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e", size = 2388704, upload-time = "2026-10-11T18:33:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b6/bc/2e24c8415eae1a25ee5a5484946a917123bad2e9d01689a759236928175a/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b", size = 2464118, upload-time = "2026-10-11T18:33:44.856Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1f/945b8053cb061c64e102bcaf7bfb9ed740c0bd4349f9bb978a7d4ddff4ab/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019", size = 2150142, upload-time = "2026-10-11T18:33:46.661Z" }, + { url = "https://files.pythonhosted.org/packages/2b/78/96a3e50bf0d64aaae781107eb9335b7529d05a85793ed6e7241c4cd1d931/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112", size = 2245577, upload-time = "2026-10-11T18:33:48.574Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5d/a6038a0322232758a6ebfa709f4ca14a60bf5b93940cd0b345056a557da4/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a", size = 2263728, upload-time = "2026-10-11T18:33:50.527Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4b/76ded3333a457a9344c4f2d63f2d82d0101654b05178fa4ddfe7d3627674/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb", size = 2293630, upload-time = "2026-10-11T18:33:52.362Z" }, + { url = "https://files.pythonhosted.org/packages/a9/00/9eca378335c9c1b72bc779bf6e4c2a82ce784f14ec48a0e87102c9870e05/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b", size = 2468048, upload-time = "2026-10-11T18:33:54.118Z" }, + { url = "https://files.pythonhosted.org/packages/35/ca/e3832e9cf93651251de43c8c5c029ed680a3c1a0a1b17ee26c81bed08cbd/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807", size = 2512159, upload-time = "2026-10-11T18:33:55.99Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f2/773469b5a10a39116a2cb17edaf6d722f017dd8da07161b371465311c542/pydantic_core-2.50.1-cp314-cp314t-win32.whl", hash = "sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19", size = 2098406, upload-time = "2026-10-11T18:33:58.043Z" }, + { url = "https://files.pythonhosted.org/packages/ee/42/0bb74f8f25204b259b11ab7c12dc7f180893b6bd706118b385147fb6efd5/pydantic_core-2.50.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c", size = 2148481, upload-time = "2026-10-11T18:33:59.913Z" }, + { url = "https://files.pythonhosted.org/packages/47/0d/d801646c9679a4e630e15cf521d4108b93854b42a6e6e02391bd4c6b1095/pydantic_core-2.50.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86", size = 2174972, upload-time = "2026-10-11T18:34:01.875Z" }, + { url = "https://files.pythonhosted.org/packages/b2/84/23984b763d8862a02a13d27a44b6e8169428fd85ecfed88f54c29108604d/pydantic_core-2.50.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102", size = 2224599, upload-time = "2026-10-11T18:34:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/4d/90/a63cf8586abc1d1a3f6d9b18f0224789ebf003851f092ebda3c7c863fd32/pydantic_core-2.50.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c", size = 2024191, upload-time = "2026-10-11T18:34:05.901Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6a/f34bff9808ffb4907fbf5f5040457d253cacf2da7fce9efbc99ca1b1a44d/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7", size = 2048252, upload-time = "2026-10-11T18:34:07.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/54/13f419bf1eb59852003818e25935aaf175687d978f4c4bca70e08fe40a3b/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43", size = 2173376, upload-time = "2026-10-11T18:34:09.592Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/b5a34d24cd0c81669d8f8339d74e6815abcf2f8fb48ab4b49b84c09be1d5/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78", size = 2364559, upload-time = "2026-10-11T18:34:11.534Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8d/d64d6216a8df365082665927ff923f183056f9049fee08e9777c9ac05296/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831", size = 2444697, upload-time = "2026-10-11T18:34:13.535Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0d/1b1149f60a00ea21ba5f70e28acbd40feb4598af414f80f53c921fac07c9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa", size = 2159961, upload-time = "2026-10-11T18:34:15.56Z" }, + { url = "https://files.pythonhosted.org/packages/1e/35/f236549299dcc78e71e945495d7ec67e78d20844d0999c60601685003031/pydantic_core-2.50.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5", size = 2234581, upload-time = "2026-10-11T18:34:17.502Z" }, + { url = "https://files.pythonhosted.org/packages/6a/85/26901a490522b7f75ef9bb9a7afb73e5bb550f0e1cb5b99a0069183e2eb9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415", size = 2244090, upload-time = "2026-10-11T18:34:19.402Z" }, + { url = "https://files.pythonhosted.org/packages/ca/2c/481bcfc70ceeb77a778ca6e5b705fe592cb64735c108157f81b7dd9c280e/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10", size = 2277948, upload-time = "2026-10-11T18:34:21.317Z" }, + { url = "https://files.pythonhosted.org/packages/24/eb/f1e09333faa7ba447cde967310f758430cc7c5e987bdab5228816c70030d/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f", size = 2449688, upload-time = "2026-10-11T18:34:23.321Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b3/036bde636db8f76d92996e81aefc75678ab5cec4a07eea1ad0c72a893fc3/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459", size = 2479286, upload-time = "2026-10-11T18:34:25.214Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/07f018294ee18d144afeb6df1a47d9e92960f014be45c5ed13519fa5af95/pydantic_core-2.50.1-cp315-cp315-win32.whl", hash = "sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290", size = 2103683, upload-time = "2026-10-11T18:34:27.42Z" }, + { url = "https://files.pythonhosted.org/packages/5f/98/f9bd7e1f9b6709f155acb9ef826d9c3884fe925f811fd8e55b9b52280bad/pydantic_core-2.50.1-cp315-cp315-win_amd64.whl", hash = "sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed", size = 2171681, upload-time = "2026-10-11T18:34:29.508Z" }, + { url = "https://files.pythonhosted.org/packages/10/87/4bb3e1e7f385c076ab5af4d6dd0571b22042cd8207eb810d5b9fef15ae31/pydantic_core-2.50.1-cp315-cp315-win_arm64.whl", hash = "sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54", size = 2156251, upload-time = "2026-10-11T18:34:31.455Z" }, + { url = "https://files.pythonhosted.org/packages/47/47/83643225b08f2aef6c8cc4bbe6e3f79c5e139c4364a6e450d0f399d87774/pydantic_core-2.50.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a", size = 2234363, upload-time = "2026-10-11T18:34:33.65Z" }, + { url = "https://files.pythonhosted.org/packages/5e/66/127ca649ba2f2462039dc1e694c6c01e00a3689f023a617364d3507e6d97/pydantic_core-2.50.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08", size = 2000972, upload-time = "2026-10-11T18:34:36.037Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4f/e421e0a5d653b1203b090b2e48745976988d7338a647940704b5b9c2b399/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe", size = 2022925, upload-time = "2026-10-11T18:34:37.972Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/ea5568e2491e1a71100f15ae8c2d01ef52db42a184a2d4e716bc79e5eb8f/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f", size = 2192867, upload-time = "2026-10-11T18:34:39.921Z" }, + { url = "https://files.pythonhosted.org/packages/ae/5e/3b8c3a35acbe219909ada5defad5d7d9fed845fb2b37bd5ec518f453c119/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8", size = 2388687, upload-time = "2026-10-11T18:34:42.184Z" }, + { url = "https://files.pythonhosted.org/packages/b6/aa/7889b4e515f91a2e8c0ae6b5081fec0feb30c4398d1434e14793c60f173a/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a", size = 2464415, upload-time = "2026-10-11T18:34:44.384Z" }, + { url = "https://files.pythonhosted.org/packages/08/78/93449e628eb8a6fdcce3eff9043081179d1bc7ce6f1bff32dc5006f41e00/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8", size = 2150823, upload-time = "2026-10-11T18:34:46.392Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f1/72c5bc129fceb0d00f05dc1e67f518c1728de1928c55f81fc13d7690de39/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709", size = 2245806, upload-time = "2026-10-11T18:34:48.805Z" }, + { url = "https://files.pythonhosted.org/packages/28/2a/922a0e78f3aa6ab837b59f88190233fb8dad546c17995fde9bb3ed3b9b49/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2", size = 2264160, upload-time = "2026-10-11T18:34:50.876Z" }, + { url = "https://files.pythonhosted.org/packages/52/a8/0f1449e3e1b20941c9372faa18e7b6a092e30cdfa841902473f7989532ac/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a", size = 2294031, upload-time = "2026-10-11T18:34:52.876Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d0/1031f492857de70355fb16524bbb03efce5fd34c93ed4a1ec60be07e0d4b/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f", size = 2468423, upload-time = "2026-10-11T18:34:54.995Z" }, + { url = "https://files.pythonhosted.org/packages/46/52/269ffffa645b8e47906395a39cf9db1151ae9fa4bcd7f47b960bc31baf37/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b", size = 2512598, upload-time = "2026-10-11T18:34:57.149Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/e47e28281f20326ff6f3c31d626f0a83e615d2d94ba41bb0ad6ec237184a/pydantic_core-2.50.1-cp315-cp315t-win32.whl", hash = "sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f", size = 2098535, upload-time = "2026-10-11T18:34:59.313Z" }, + { url = "https://files.pythonhosted.org/packages/03/ad/759e181e69c1b472c60b2049e5f61d5da1deefdd5ce1df4bb2ebcf771f25/pydantic_core-2.50.1-cp315-cp315t-win_amd64.whl", hash = "sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b", size = 2148645, upload-time = "2026-10-11T18:35:01.571Z" }, + { url = "https://files.pythonhosted.org/packages/6d/56/8a702c27e5be9f47e5f19d8669227424290e4c024e7c370279cbaf244b4e/pydantic_core-2.50.1-cp315-cp315t-win_arm64.whl", hash = "sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44", size = 2175297, upload-time = "2026-10-11T18:35:04.079Z" }, + { url = "https://files.pythonhosted.org/packages/a5/09/1bcf160f3cd6333e2a76982fae5963e5032de0bcaaba38464223dc266bfe/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242", size = 2217070, upload-time = "2026-10-11T18:35:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/3d/81/f02de95eec7794dfc7a5bc69a834f0e834b31b3009014ce6de903855f775/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5", size = 2039027, upload-time = "2026-10-11T18:35:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/4d/16/227746ed3771d9bf2304568b55134849ece34e13b38b1d76de763015c77f/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295", size = 2109009, upload-time = "2026-10-11T18:35:20.218Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b9/7664d1592a0e5a74903f357cd5b28ceb7336f3473256c5a4e8c8432f1881/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61", size = 2269883, upload-time = "2026-10-11T18:35:22.48Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] @@ -525,9 +760,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901 } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801 }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -538,9 +773,9 @@ dependencies = [ { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087 } +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075 }, + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] [[package]] @@ -552,9 +787,22 @@ dependencies = [ { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328 } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "python-dateutil" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/df/f15f80383f152452d8985b36d95cb8751f901a3e0ff4cd27f7e118f168e2/python_dateutil-3.0.0.tar.gz", hash = "sha256:7ebcb3c29a92f3ceafe79a7a48a5c45677e9e004bbeec5742ac0bbb00560b7a2", size = 271066, upload-time = "2026-10-21T23:41:45.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424 }, + { url = "https://files.pythonhosted.org/packages/5a/17/cebee01d1c0c1441174cf0456ddc91b280d79acb65340c05e41d77866ee8/python_dateutil-3.0.0-py2.py3-none-any.whl", hash = "sha256:3c79630202e4152c6add678446984236efe4f57e84d8e892aab0637bab82e9fd", size = 94659, upload-time = "2026-10-21T23:41:43.431Z" }, ] [[package]] @@ -565,64 +813,64 @@ dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/16/6f3f5e9258f0733aaca19aa18e298cb3a629ae49363573e78d241abeef59/python_discovery-1.1.2.tar.gz", hash = "sha256:c500bd2153e3afc5f48a61d33ff570b6f3e710d36ceaaf882fa9bbe5cc2cec49", size = 56928 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/16/6f3f5e9258f0733aaca19aa18e298cb3a629ae49363573e78d241abeef59/python_discovery-1.1.2.tar.gz", hash = "sha256:c500bd2153e3afc5f48a61d33ff570b6f3e710d36ceaaf882fa9bbe5cc2cec49", size = 56928, upload-time = "2026-03-09T20:02:28.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl", hash = "sha256:d18edd61b382d62f8bcd004a71ebaabc87df31dbefb30aeed59f4fc6afa005be", size = 31486 }, + { url = "https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl", hash = "sha256:d18edd61b382d62f8bcd004a71ebaabc87df31dbefb30aeed59f4fc6afa005be", size = 31486, upload-time = "2026-03-09T20:02:27.277Z" }, ] [[package]] name = "python-dotenv" version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135 } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101 }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -632,9 +880,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127 }, + { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, ] [[package]] @@ -645,88 +893,105 @@ dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458 }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] name = "ruff" version = "0.15.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214 } +sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185 }, - { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201 }, - { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752 }, - { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857 }, - { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120 }, - { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428 }, - { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251 }, - { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801 }, - { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821 }, - { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326 }, - { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820 }, - { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395 }, - { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069 }, - { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315 }, - { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676 }, - { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972 }, - { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572 }, + { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, + { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" }, + { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, + { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, + { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, + { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, + { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, + { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, ] [[package]] name = "shellingham" version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "solana" -version = "0.36.11" +version = "0.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "aiolimiter" }, { name = "construct-typing" }, - { name = "httpx" }, + { name = "httpx2", extra = ["http2"] }, + { name = "pydantic" }, { name = "solders" }, - { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/66/b8cd6e4d95bfe46798942ace31935e7799005a4e2180869dc7bac6b75be9/solana-0.36.11.tar.gz", hash = "sha256:2fdcf483674f4b88fe6510524bf3234a5837d19fe1815aa5a285f2739d28b3a3", size = 54516 } +sdist = { url = "https://files.pythonhosted.org/packages/53/ad/70c3f71e3452b157ee160593483dd9c33d5c874157b670e467901716bddf/solana-0.41.0.tar.gz", hash = "sha256:a8f3b31789256c69c2c0e260f00a4d540627cc3e860f0c4462342ead3390eaf5", size = 511405, upload-time = "2026-10-03T14:05:43.558Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/8d/807eebf0560759ad90464060e0d1d87ff5409beb6ed56104c553a83a976a/solana-0.36.11-py3-none-any.whl", hash = "sha256:1d659decc67a40ee1e9b5ded373a076b87cf3b4bd0645e120d16d9348c2025ba", size = 64786 }, + { url = "https://files.pythonhosted.org/packages/ab/05/c6795aa0f5240b7698475e6434c0e1dcaa7657b55cb6361cf4859886a3fb/solana-0.41.0-py3-none-any.whl", hash = "sha256:7b8efcef938d75b94a62e995383f59f68b566b0a01ad1694ca62591560da7e47", size = 52302, upload-time = "2026-10-03T14:05:41.988Z" }, ] [[package]] name = "solders" -version = "0.27.1" +version = "0.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonalias" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/25/80a81bb3dc4c70329dd0016edbdfbf2e8d8300a98ab9cd1a6ea0266bda7c/solders-0.27.1.tar.gz", hash = "sha256:7d8a24ad2f193afcdc02d6f3975917a7358b0f0ab7f4b3695b135ff2008222c8", size = 180923 } +sdist = { url = "https://files.pythonhosted.org/packages/bd/83/c73a87e1b3785cc4a95c9fc3aae33c66c55e7653f1a9e7d0889dc55e5ba7/solders-0.29.0.tar.gz", hash = "sha256:c17098cab72a1c202ec4cc0ce3ecbff4296c9de97000e05944337ba5ddb681a8", size = 178847, upload-time = "2026-08-13T17:26:09.846Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/6b/0c0ee4766705824261779d00229fb95308d6b28422613e0e2af577f60ee3/solders-0.27.1-cp38-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:4dcd8e766bab24afbe9e0ae363d86f9810457e04b00c8a9149f69ca939ed587c", size = 24883435 }, - { url = "https://files.pythonhosted.org/packages/33/1c/be04a1b26e18c409dd006d214198dc03f0b657c1cb34f4c83b763f8348f0/solders-0.27.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5d87b145cc0129095f9cff8c7f28d2e910bc5b5a4cf257c263b08a4b95f111dd", size = 6480729 }, - { url = "https://files.pythonhosted.org/packages/48/03/98dc73c266b11ed5c13b3933510a1aa115becf97f45bec1a22da9d03ffa9/solders-0.27.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6082bbe46b7b1b2b005d046011f89fcae75fc5ea4f1a0ef5c2e9dfb5fe7930ce", size = 12744782 }, - { url = "https://files.pythonhosted.org/packages/a0/39/35384d8fb80d05937bd9e8af7237cfe3f0d017c8aba357209d90d428f3a0/solders-0.27.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ccb821c2e4af43d976f312086f248a67352b3986e5f4c87af41cfeac6d8b5683", size = 6601257 }, - { url = "https://files.pythonhosted.org/packages/8c/65/8989e521142473bf1130613476a4449e106bb97ed6cc86097f6f519b1234/solders-0.27.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:663a10566ae81f67c4515d4db5fbf51b735204741728c1a5cde11c4e019a51df", size = 7277802 }, - { url = "https://files.pythonhosted.org/packages/f2/41/87ecf12cec0e7aa9c67b0cf1b8079fb28aa0af91e97328a3bd0c5e3001ba/solders-0.27.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d14f05a77dbbf7966fb26f255c81302e6127550bdb66c2fdc99f522043fdf376", size = 7082541 }, - { url = "https://files.pythonhosted.org/packages/33/b9/35e6f59b41bb205b26c7318fcdca43f3d59464fd3ddc13d36f36427f64d4/solders-0.27.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f778eeab411acec0a765a01c7b772f8eca8a8543d98276bd83cb826960da211b", size = 6845568 }, - { url = "https://files.pythonhosted.org/packages/9b/f3/14ed12d8d5047ababaca3271f82ebbf500ff74b6358f283962232103a12d/solders-0.27.1-cp38-abi3-win_amd64.whl", hash = "sha256:f3b787c29570a46d219c7a67543d8b0fadc73abda346653aa20e8eccd839e78b", size = 5295092 }, + { url = "https://files.pythonhosted.org/packages/63/1c/37586514dd9ff1a861da0fe685257aaf3ddf86db374d35e27bff6eee4175/solders-0.29.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:785841623bdd7b8dedc68e7c794b54327d3b03e1ab9697eda90a139ca3c98cfe", size = 22898706, upload-time = "2026-08-13T17:25:49.924Z" }, + { url = "https://files.pythonhosted.org/packages/a9/16/014a7b58d12332420947ae49ed7c7efd030c196d64d58a2944ebdc0f8a88/solders-0.29.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00524baf575ae5edb2ba43cdb1d80491c59efdc16b48fec96a6f929c0401d0e4", size = 11922708, upload-time = "2026-08-13T17:25:57.574Z" }, + { url = "https://files.pythonhosted.org/packages/80/d9/5176c28534ae3da529fb995cf5e56fbd92731608bbeef2c908168a9a9b84/solders-0.29.0-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:62e8a5bb4bc8628d5fe49faa56aca149dce284a9f8bf5f3c56c4721e561be84b", size = 12427749, upload-time = "2026-08-13T17:26:00.234Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7e/5ced3f65870efd796d00a22d5aa9f202bdfd8ea6a4c4d6993baf52305c64/solders-0.29.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d2e011ff1d5f82a7de81d6eb89f54d002189f50ee48d4a15d7b51849e04b1c3", size = 12552807, upload-time = "2026-08-13T17:26:02.822Z" }, + { url = "https://files.pythonhosted.org/packages/46/a1/68e47cef35fb1c8433fbdc9df802382ee8c44ebe35d9d6cb2995b2077453/solders-0.29.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:09e80c545c728f434f627d3b4714675a0f654c4d387f3339d54f7d09d59f3f1c", size = 12504249, upload-time = "2026-08-13T17:26:05.373Z" }, + { url = "https://files.pythonhosted.org/packages/dd/9c/3db8a38335ff201ea09c2a6fe2f1788ebe4cc5651f97c4953a2011bebdf7/solders-0.29.0-cp310-abi3-win_amd64.whl", hash = "sha256:f36db7b43088866081b5d37ff62668551e30dd136b9ee58a5fb524fcd828082d", size = 11040172, upload-time = "2026-08-13T17:26:07.961Z" }, ] [[package]] name = "tomli-w" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184 } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675 }, + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "truststore" +version = "0.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9f/c5201d42a484c061e528825fc8e2d565f5abd50a4ced6fb7d29c4ec99b2b/truststore-0.10.5.tar.gz", hash = "sha256:30d36967ccaded5cbb38d602c433f53600036c79d502f4533a49b60a03bbefcd", size = 28091, upload-time = "2026-10-12T22:27:31.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl", hash = "sha256:9aaaedaefaf06d8b206278cf8b5012bc897f485a874503501e12d776df78951c", size = 19017, upload-time = "2026-10-12T22:27:30.377Z" }, ] [[package]] @@ -739,18 +1004,39 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085 }, + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, ] [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/68/f1b440335057bfce71b6e50a9d09445aa2ecbd08359a337976627b8409e7/tzdata-2026.5.tar.gz", hash = "sha256:8cc73c0a0bfca7dbfa59235d60b2eff82231dee33f53d206db1acd9173cfc0a7", size = 200404, upload-time = "2026-10-03T09:23:14.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl", hash = "sha256:b683bd1b6659ddcd810ff02ad09ba821d4bf1065072805063eb35c49617905ac", size = 347996, upload-time = "2026-10-03T09:23:12.535Z" }, ] [[package]] @@ -763,38 +1049,142 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084 }, + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, ] [[package]] name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437 }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096 }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332 }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152 }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096 }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523 }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790 }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165 }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160 }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395 }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841 }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, +version = "17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/4692be6d5bf739ed049b3813901c42091f8c054df89d5c089343823dea0d/websockets-17.3.tar.gz", hash = "sha256:1c7dd93a1c9f89297471493674b1fc1d9467cafd45374f2172213046bdb3a329", size = 188878, upload-time = "2026-10-19T16:58:05.813Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/29/902ffe19c0d35603102f31490dea342f083bc77d8f8bcdc7d8b16f5810f9/websockets-17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:41665c7ea01d3ae497a0c61fd22d3424230d18782a5fceb79f04db9ccb2042c9", size = 218250, upload-time = "2026-10-19T16:54:54.718Z" }, + { url = "https://files.pythonhosted.org/packages/f0/66/f56a2637b1aac263205b0e3cd7b2ff2e2f8b404b8118c991f4d122fd381f/websockets-17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8f7469408740cf06a79b01605f2ae0b2acc8839c03fcbbf05da49baee12d3302", size = 215927, upload-time = "2026-10-19T16:54:56.124Z" }, + { url = "https://files.pythonhosted.org/packages/22/7d/a27f25451c4cc62141992b51005688481210f3d3e63de3dc66f9e882ba59/websockets-17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d68da281ac208b6236ffa8beef23fe7c7c956d8bf09b421d447232fd94441b35", size = 216196, upload-time = "2026-10-19T16:54:57.538Z" }, + { url = "https://files.pythonhosted.org/packages/9f/77/40bbc1a5c016cf9f598dea21d1250a93245c995eafaec6401d0e4eb4e647/websockets-17.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078d1d41f45ee34eed5a4f3bbe7c588caecbdd6e12dfca73e8c1b0e803773793", size = 225516, upload-time = "2026-10-19T16:54:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9c/91889c8c62fb7d16cd58e3074473a259303d6229bf0ce496aec87e66f3dc/websockets-17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7f391838e8adcf1f2fe71561d9e5a9f74ea7ffa5052eb2db329aac677383136", size = 225795, upload-time = "2026-10-19T16:55:00.438Z" }, + { url = "https://files.pythonhosted.org/packages/b8/29/2e5e2158e9920f806cd45f3cbc81cf5f3eef2b9e5a74b9ef049e2a3578e3/websockets-17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c15a165673bb3fb57facbf040edf5c0556d20ccd2e0ccc5f23ebb69fb715fdc1", size = 227034, upload-time = "2026-10-19T16:55:02.065Z" }, + { url = "https://files.pythonhosted.org/packages/b6/af/fbab5c4098b26026b877bfc139b1ea0d7c6eaf7a86b84544933b4c445f82/websockets-17.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28d1935c16782a1e3650c9baa79069b016fff739e292e4cb14bbb830d6930491", size = 229863, upload-time = "2026-10-19T16:55:03.415Z" }, + { url = "https://files.pythonhosted.org/packages/ac/72/d6e460a991607794072fe3fdfcb3221a0342af61586f5bd7ea40bddcfd21/websockets-17.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c534c9524a2acd0b97b7022074bec3784ac273c683cf9535087e437672ea8531", size = 227669, upload-time = "2026-10-19T16:55:04.763Z" }, + { url = "https://files.pythonhosted.org/packages/ec/8f/1c7da1cfb632e141e019cf2e0bbbd4e6dc8c198c8cc29544e4acf1a89f56/websockets-17.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b459d29c8b82a420a98fa0ce8e9cf787ce0062d98cc90604ef9574fb3cc26ff8", size = 226429, upload-time = "2026-10-19T16:55:06.112Z" }, + { url = "https://files.pythonhosted.org/packages/3e/63/c48ef8bbfbd982f2cfae1cfcf9e63f5bfbb8468fd37ca700507efc0b0007/websockets-17.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53209dffd91b0aefed9ea630fa6ad5c2944d6210ba3a8a3a8664dc1575289b14", size = 223653, upload-time = "2026-10-19T16:55:07.473Z" }, + { url = "https://files.pythonhosted.org/packages/cb/37/7bedaad48ae8e89eaa4f3942c11be0c41de77ce2118a475f3ba05e9b5a0f/websockets-17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9662a9fe457820c60365b3209e03cbd8e4a817195d55de65a80fa680b4bea136", size = 226465, upload-time = "2026-10-19T16:55:08.791Z" }, + { url = "https://files.pythonhosted.org/packages/33/cf/ab9a8a0de4769ca1dce392021b6a922e21b4d3687fe9e1ab4db585a088f8/websockets-17.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5670a762c7ae388bf8aa04199fd436180a4d28318dca9c1eae7b38604676dbfd", size = 225071, upload-time = "2026-10-19T16:55:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/89/96/64d8460cec95f873c61c6d13a09b435915150640525ae56d5871400aec09/websockets-17.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ed59cb4ee258674a6ed9b8515e42079db02cb3be94f133fdd3f200183a40c49d", size = 225841, upload-time = "2026-10-19T16:55:11.519Z" }, + { url = "https://files.pythonhosted.org/packages/a6/98/2b83211ee3087db4ae66533782664c0e283cf4a08cbb14036018d0803e09/websockets-17.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f78bb2e82fe88912c9d7b25c98e57a9b08655579af89f164fd4c0657a075d62", size = 226945, upload-time = "2026-10-19T16:55:12.85Z" }, + { url = "https://files.pythonhosted.org/packages/9f/fe/6b2a37d422d7f4dfc0c92c95a14649d9bd662243ed8b0774523ed2703604/websockets-17.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:71250e010eede33c1d638e2c7a98288520d67f782fd5eceecaeca3129233a09a", size = 224502, upload-time = "2026-10-19T16:55:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c5/364015c9311c89ea6085d54c70d654c5d27ba05014d44ef9fd9daa09462b/websockets-17.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:729989759feb81ff6493879a0d60a0bd7d2eee41001bbec0381a080318439382", size = 225483, upload-time = "2026-10-19T16:55:15.899Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b3/75d0cae8398816ed34a16a78252bfc8651f2f883cc5a568e1198395f15df/websockets-17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90d8c4fa5ba293cf92de351fbc292cdaadb1a1d58d031a1a97ff9b8fb6250ec7", size = 225761, upload-time = "2026-10-19T16:55:17.28Z" }, + { url = "https://files.pythonhosted.org/packages/f0/af/9a7c4b2e87f47a2a6a066acdc2157e9b7cb6222a224b95a432f54ae786ce/websockets-17.3-cp312-cp312-win32.whl", hash = "sha256:0824e6ecee838cc28e6481062574c7fde49f9a2cedfacd36f0e91cb4b37fd6fe", size = 218722, upload-time = "2026-10-19T16:55:18.658Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e8/e52953a8280a1e51cb9307218443f68c8f4dc4b9952db6021d5c12d03c0f/websockets-17.3-cp312-cp312-win_amd64.whl", hash = "sha256:6644eecd99c4e8cdf72e646e17b391bb70b054f44becde37dcdc48cbec7e1527", size = 219024, upload-time = "2026-10-19T16:55:19.987Z" }, + { url = "https://files.pythonhosted.org/packages/85/a4/005eb7c1f5c1a249c1231a6ed83a54ada0b6b8ffab33a608ca20e3034ea0/websockets-17.3-cp312-cp312-win_arm64.whl", hash = "sha256:4a9133dbaedc6f1af358127275676171d9a1bb3e61f47ee62b2ff9acb78af3d0", size = 218950, upload-time = "2026-10-19T16:55:22.161Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1d/c1f4a1c45c5ca2c35ae9fd7cac0e491f8a014c9a86933a5da7b04ff825cf/websockets-17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:52a2ca6f401fb49a8d5d9e0e48ade5f58f1bdda33132907b0e4e3b702628fa4e", size = 218243, upload-time = "2026-10-19T16:55:23.851Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e8/f1b47d0ced4964ae748931a54347fd27b06b62e8ea7837a9de3aee0c5999/websockets-17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80578e91764bfca297ddca322c00a3136d993b829e9839e7fa378a19c1fcc25d", size = 215927, upload-time = "2026-10-19T16:55:25.317Z" }, + { url = "https://files.pythonhosted.org/packages/16/df/ab0ed0445d8d81a133c8fed760f3b4f743a2f563804fdbecb2c32fe05d94/websockets-17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fe7a168d805c8a7bf1c2de55b7ab2bc958586bfd88d0e87235a92cac66b590a7", size = 216185, upload-time = "2026-10-19T16:55:26.678Z" }, + { url = "https://files.pythonhosted.org/packages/9f/51/b30fd6c5a33dac76a78083be125e3f9804f3d735da494ff03a46834f31f4/websockets-17.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:54fcd24944e6d127d198c28dc363967b7131ff0c97833962c2ef2048018de02d", size = 225574, upload-time = "2026-10-19T16:55:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/1c/8a/8ede659919c0805d1fcc79c2ed16588d3419f1687c5117ba46f0217750ec/websockets-17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de2c12555bf05546f3ee75733f2c81de70aab7f530e86d0d2162b0ddcb3c39ae", size = 225854, upload-time = "2026-10-19T16:55:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6a/e75d10e3896edc61320062890f71cdc0767dc4a54e2a26a943b703042d13/websockets-17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c95d25b5382ee0e6191ecafdeabefdc1f7bff8dfee29239b9b79e760ef3b04bd", size = 227091, upload-time = "2026-10-19T16:55:31.146Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d3/658f1e153566e5edae02812122d9f020c30785d8b53c4bda61b8e976f77f/websockets-17.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8f244dfee05ea29b8064701880865eb1684f3a704a5665bd10b64694444dbbd4", size = 228526, upload-time = "2026-10-19T16:55:32.524Z" }, + { url = "https://files.pythonhosted.org/packages/28/00/002f95a02d721d8c6ec2676c5aee904a823838a9c205fc0b31eedd882a7b/websockets-17.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e99440ef446c7833690c1754e23b0188d440f3cb89812b0764c697a7880cb33", size = 227718, upload-time = "2026-10-19T16:55:34.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9c/d36ec9499a62ec4e2a4eb570f93c6fcedd80ad5685e0f9d6866e8cfbbb84/websockets-17.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:26838e2d95d7004c52e8c7b6b51676c872093a3b55a497824f899116a15a8a55", size = 226472, upload-time = "2026-10-19T16:55:35.484Z" }, + { url = "https://files.pythonhosted.org/packages/36/5d/3a6d74e409f24ae4e8661a9d74d6645f71b3e91425a527c0e3f2e78bb15b/websockets-17.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:508775efa11d26aec186d10154359f52276e2baf9ca201b52356330883276dfd", size = 223710, upload-time = "2026-10-19T16:55:36.853Z" }, + { url = "https://files.pythonhosted.org/packages/e1/90/e08eb5d69b73cf5babf80922a8c964019b48339ce5c694a5d0c0f083c5a8/websockets-17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2789d203babb6ceb2770aadc2394c1f99263ec37fba57a93064f9a8ad899e8ff", size = 226532, upload-time = "2026-10-19T16:55:38.191Z" }, + { url = "https://files.pythonhosted.org/packages/c2/45/b1916efa1dad5af33beafe5c536810e1c80856b7e913f671c35031c57745/websockets-17.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfef7f7aa434a1e632e0ebda14b39f4f1d7975e0fce5c77ea7b43156fabf9bd8", size = 225129, upload-time = "2026-10-19T16:55:39.635Z" }, + { url = "https://files.pythonhosted.org/packages/04/c1/d427abb3f11e92f102f70f8e73addb767fc62c614565c0ee3f5657fa3551/websockets-17.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:39f532fa0d9026de4b9315a175998646112776baad54d55348435c11b05d034e", size = 225900, upload-time = "2026-10-19T16:55:41.033Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/462fab8e4b09eaa4eac2f40fcff8ee30f851aa40fb518efb0177262f6590/websockets-17.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:85d6daa3a7ca404570ae130babbeb347ef9d3faca5300fb13cfa4bb48c3d30d1", size = 227007, upload-time = "2026-10-19T16:55:42.314Z" }, + { url = "https://files.pythonhosted.org/packages/71/36/7404a4376021a0cc7ae9fcbdb1196c0c8aa48df4fd370c185f4f5b677bff/websockets-17.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ce39283ef08b0f28478e920d4d89a1fc169176e568e8a6a821332673145522eb", size = 224565, upload-time = "2026-10-19T16:55:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5f/b8ada5787d11e6edbf58e2d680235fb3b2fb3298991cc310566f4d6c2fbf/websockets-17.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:23046098ebf844753cfd211033624a8247520e5bf2194c24cb1a8c222917acff", size = 225516, upload-time = "2026-10-19T16:55:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/23/1c/0533f760237ac6604efd0bf98c18ea4c1381f2cadd8c8d0b05faecb07a80/websockets-17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:724224d4c457207271239114bf43afafc3eca4a1b4661ccdd428d11869a03bbd", size = 225797, upload-time = "2026-10-19T16:55:46.989Z" }, + { url = "https://files.pythonhosted.org/packages/97/b4/0e580e8f96197130ebdb32b727e9c891594c930be30d8a9de9aa0bbb7e2c/websockets-17.3-cp313-cp313-win32.whl", hash = "sha256:5af7070d80d278832cbca9c3c3dd44b30a26dcfa3ae1e0f6f19ff34edc27b689", size = 218712, upload-time = "2026-10-19T16:55:48.379Z" }, + { url = "https://files.pythonhosted.org/packages/7d/6c/db5c907d1cbea84444bc759ff63db97fc586988d11a34d5565e8b50080fa/websockets-17.3-cp313-cp313-win_amd64.whl", hash = "sha256:66027e401c70d0cf55021b91b8a7ec2cd5810e20602f945b4fe6ae8b37ed232b", size = 219026, upload-time = "2026-10-19T16:55:50.688Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d3/30f04bec5b9cf0c00c98c038494b210ece923a0b05b6df38a162b759e5ed/websockets-17.3-cp313-cp313-win_arm64.whl", hash = "sha256:6b14914e67044724c31a9360c7b89e9d070117c630746eefdc1620d967545acd", size = 218958, upload-time = "2026-10-19T16:55:52.085Z" }, + { url = "https://files.pythonhosted.org/packages/27/ad/f65e365bbf38d61164b0c2f64776fc7714bfa59ac51e492b038a35f4ab1b/websockets-17.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:bc8fd230ef013951900c1fda100eebe4a4b1b407dde2a44186995042dda2c628", size = 218288, upload-time = "2026-10-19T16:55:53.387Z" }, + { url = "https://files.pythonhosted.org/packages/48/1c/33271eba53b4927a1b91d99d838a6d7b888e6f708425262c911450d73696/websockets-17.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cdb8e4b0f6a41b857c74c03116e8ffbef59ef8e533e934b532843178990d01d5", size = 215977, upload-time = "2026-10-19T16:55:55.063Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f3/6d982c60f6c7c95e5faed53d39263df273ecfda904c7d6920c37eee58dd1/websockets-17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:018fab26140ce221c3853454a608e4e7004f4f10641cb97819dcd5b917bb2e99", size = 216193, upload-time = "2026-10-19T16:55:56.424Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/4ff6b8d2a4b32719d4658bbd791ee9d19939a47a69b961c961fd9dc3a64c/websockets-17.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd0f3037d501178a761fd2814370aca547f79e99a200a61a1f6e7b79ec3d78b9", size = 225573, upload-time = "2026-10-19T16:55:58.252Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8f/46e4030947f5590cb61832901f44a3edb07ef8d1e27f6c06b42c5f1c4d33/websockets-17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8dbb3862aa649de95660b6337d97101977f160b5b354d07b82fe309874d2c8bc", size = 225927, upload-time = "2026-10-19T16:55:59.561Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1b/3db30fdf4fbefa4ac463060d3f87e2d444f960aa1234028468c09b88bffc/websockets-17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af37a1e6b6bd506b597d5fb4cf111fb527d1b2223608a5d441d4738827cfd166", size = 227169, upload-time = "2026-10-19T16:56:01.029Z" }, + { url = "https://files.pythonhosted.org/packages/02/ed/849799cfc0e3e359b75ca1d50605376da52dc9ef0b2a53931e3f5991f96e/websockets-17.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:740bba4223fa5216d4d6c5dcd29e697f37229b8a61b69e1a50527b0ea993ab2c", size = 228542, upload-time = "2026-10-19T16:56:02.377Z" }, + { url = "https://files.pythonhosted.org/packages/19/53/dcc397e20ce792fffd9a8f79b6333275748ee7a23f807e576166a0b16edd/websockets-17.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4170556762c6fd2913d99dfdd8dbb4c927584899df7facb1034627e355263c45", size = 227772, upload-time = "2026-10-19T16:56:03.768Z" }, + { url = "https://files.pythonhosted.org/packages/c9/36/e5935782f71e9e22247b2a2e562da05e18d8945c49f2692ca1bdaceec178/websockets-17.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d355b22270be8062360b9df2ee0b10277590789a1059257916fb9e256a0cff80", size = 226518, upload-time = "2026-10-19T16:56:05.332Z" }, + { url = "https://files.pythonhosted.org/packages/27/76/bd5978a1e403fc646230cf35cf7c06b40b3a232d440881b9e89ede229b18/websockets-17.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4aa85b34c21cf97b3470793d0bf0763d32bfd1e67b83067a0a3ba8dc3474dacf", size = 223766, upload-time = "2026-10-19T16:56:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/da/87/c66eb39bd851d5c14d9eb75aa64d03cfcf2ded77927ca067b5917291d19d/websockets-17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a0e2e7c2c1a4f25db7b6596738d810c10732e77f852247300a1bc49e364d6dc9", size = 226644, upload-time = "2026-10-19T16:56:08.198Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/b9a31d5497fa0b73629a097b173a26123da2ed656b562114644bc623414b/websockets-17.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7fa69212ea60ea24e2d8aff23fe102b1f87f1b27a320f92d2e7b3468997a496a", size = 225107, upload-time = "2026-10-19T16:56:09.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/0c/b773a102f84b60b2052b2cfde773d2f3cd3e8eaa93a145f54450460549cf/websockets-17.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:80a6e37e235780f189558ab804f3cbc01c18f5c578995739b49613d544d34ebf", size = 225891, upload-time = "2026-10-19T16:56:10.973Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/e7e538575dd4912dea9ea4c9ae86519aa1420969d9cb5cf02fd10fd20ba7/websockets-17.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:628efe13630ac3397e7d1278b6be6f22f37a86d638a324334b4cd43c1daf9928", size = 227064, upload-time = "2026-10-19T16:56:12.295Z" }, + { url = "https://files.pythonhosted.org/packages/28/87/bf2b928c156deefd0c50285a48fc985e2ca85d284e7d9981cc5b79b0e696/websockets-17.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:443fa28652682d2a3ec3eca49af1b87cb2523561257683e62e03a98967b22138", size = 224618, upload-time = "2026-10-19T16:56:13.73Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/d59817ba3535f787c1cf8990a01b3d2896a7d9d8f3b5d55bed22fce595a6/websockets-17.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6705af9d1005127302c04d9f1533ba56e289336c465923ef878430e99295a0b9", size = 225571, upload-time = "2026-10-19T16:56:15.199Z" }, + { url = "https://files.pythonhosted.org/packages/a6/96/8210379dac889e16c75099aefffa200dc55dc8cb030010f309a7eb9c75c1/websockets-17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9bde5d82d7ac4859e0cb3a35e148f537a8d4e56aa43f42582b5f263982f095ae", size = 225869, upload-time = "2026-10-19T16:56:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/d4741c977fec9e6482beffece572d544d42d230f56fc3ef504f96521063a/websockets-17.3-cp314-cp314-win32.whl", hash = "sha256:be7be57097a65a8fab3d0f0a10d12931deb626a7d8082eb732b1ae41f3a202e3", size = 218232, upload-time = "2026-10-19T16:56:18.028Z" }, + { url = "https://files.pythonhosted.org/packages/33/69/2f9ce899776d0c522f7f44080cad4228a1c14ec13b0125a2efc12ebe41c9/websockets-17.3-cp314-cp314-win_amd64.whl", hash = "sha256:2b06213c8e1ce93b18d0476d7a5e647436c8f23d099888c9065e930e80f4bed5", size = 218582, upload-time = "2026-10-19T16:56:19.399Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e9/11b885666d72add674440a3435c059bea325616959cec8be42605948714c/websockets-17.3-cp314-cp314-win_arm64.whl", hash = "sha256:6b7b540bb321f7f0a7b8f5d107f941bb361e3a154c0b594dae0394997b91b743", size = 218465, upload-time = "2026-10-19T16:56:21.005Z" }, + { url = "https://files.pythonhosted.org/packages/c4/0e/0c0f40ab8eefe9d5a6c78b4e267d8e37d70d3db8dd195574e0ab867e4f81/websockets-17.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:c9d06edfd974e918b40e0662a7e76dac13125529ee5ec9fbe42dc0d6faa209c3", size = 218614, upload-time = "2026-10-19T16:56:22.457Z" }, + { url = "https://files.pythonhosted.org/packages/1f/93/d22b3d63aa439bc01a9477880070d05e0b257b0548294786a33be3e842ec/websockets-17.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a0bcf66289b9d017c175562c35c5304b5cc3ed56aded994ae30b0a9654ba405", size = 216151, upload-time = "2026-10-19T16:56:23.95Z" }, + { url = "https://files.pythonhosted.org/packages/b9/92/f60a9672cdeb8cb4f7156e6e83c76d59fdc586737a97ac77129e9fc188a2/websockets-17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f3ed747b7651668131029c720fbc32c7ee2d129bdad200e6c8f4037b20f7913a", size = 216354, upload-time = "2026-10-19T16:56:25.251Z" }, + { url = "https://files.pythonhosted.org/packages/9d/da/2be8c5f3c6612d4523f95b074e2352bc11bd090cf121d32bc11af1d52e70/websockets-17.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5868a02c4f6d698ddd19b4c9742748c03dec045289915806845189d96bc3d314", size = 225937, upload-time = "2026-10-19T16:56:26.726Z" }, + { url = "https://files.pythonhosted.org/packages/85/1c/cb84aec871840acd66dfa84520bf7b8e48ce70bdb3f4baa4efd097fbe6b1/websockets-17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa87ea2ad64e59fe5622c1dc5716106b6d6e7e4b26b848cb5f5710a75c292ef5", size = 226215, upload-time = "2026-10-19T16:56:28.1Z" }, + { url = "https://files.pythonhosted.org/packages/9b/7b/cfb65557accec2eda2c80288e13eca6f1c797c579375906eb73f4053cfd1/websockets-17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65e937576141ac50fac32fa9736dbb3e0e4a7769f91be004011a09216c6ab05f", size = 227389, upload-time = "2026-10-19T16:56:29.518Z" }, + { url = "https://files.pythonhosted.org/packages/70/a3/7e08ec4b23e376ed228831aa44b2f2a3f2e7025cc66dda4b2e53dcc2619e/websockets-17.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:35381b575f70a08d4241e73c0cde4672bb9b2a9aae8767ca6b7fb61305a052c8", size = 229534, upload-time = "2026-10-19T16:56:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7d/8a813e3e53956f876cfaad4f6358625bbd94168aa8046c44e2f3561f12af/websockets-17.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5142798871a248b422f353e0684d51dfa119de3403a6fa0904b7cb806d9790ce", size = 227959, upload-time = "2026-10-19T16:56:32.656Z" }, + { url = "https://files.pythonhosted.org/packages/41/23/5f3dab6ab44f2f30042db83a658a57129d5e3c0425c798b3c3e727bc060a/websockets-17.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e164c4a6868d11988c7f2386e3278ccff21c665cd7d08279b62709dd37a3167c", size = 226691, upload-time = "2026-10-19T16:56:34.101Z" }, + { url = "https://files.pythonhosted.org/packages/5b/49/99a3fb5a2e94d677fbd0d014a6f9a56a3d2b8e8007b7175727bc4fc4ae94/websockets-17.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10b24e836ee0233ca078e1244f6ba40343a22127a9a736f46a7e7b18de2cc711", size = 224225, upload-time = "2026-10-19T16:56:35.452Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e5/f503a4d757858d083c90ea85dd1fd636ae9eb63c9552c75a4c38514ee7ed/websockets-17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:56a76556ab04ab54a675acce2cac8b4e74ea2cec558a7c9ed37e0367b04b5d50", size = 226984, upload-time = "2026-10-19T16:56:36.887Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a6/8586844ad4b21988837624e1f4b331bc71919c85a6eb1ee3c8bd29893f27/websockets-17.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:33221e49be79d7bfe6d12bc52ef9458b256ab74fa8ac5660acfa3065de1c7870", size = 225436, upload-time = "2026-10-19T16:56:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/f419506494d6ffd205a9c3258f56ff5aeae6976de26361057de74f4ab659/websockets-17.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2fa79b9a48ae59744316c71a34cb9f76f30c93e117338522c5135cb7eac10351", size = 226325, upload-time = "2026-10-19T16:56:39.606Z" }, + { url = "https://files.pythonhosted.org/packages/28/c5/55a58faa67149e84cb98b33f9d8fda95242677ea07b78f3ecaf2e77877d6/websockets-17.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f87f02afe05c1c246012e3de100a77cfbabd17db709fe93cf074271ab239b312", size = 227342, upload-time = "2026-10-19T16:56:41.041Z" }, + { url = "https://files.pythonhosted.org/packages/35/e0/fbe561dff24b6ef1b2034877f37b56eb9b81b3c103e2a8b03819bf2b3b6d/websockets-17.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8135a549e27e9941a70e2d2156676ef2ffeff4b06e9d564dbaaae963482afdef", size = 225036, upload-time = "2026-10-19T16:56:42.433Z" }, + { url = "https://files.pythonhosted.org/packages/0d/37/d80753064f8370a24d3bec74807c3928d7b0ebef5381956e2a0108b365c2/websockets-17.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ace8dd1efa5f44c482d091aa38322e20e3ffe5adcdba6b67ad9f4e7da58f3f68", size = 225807, upload-time = "2026-10-19T16:56:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9d/a11d3168522a2959c4a5661728744d62b9eee344abdba5f9fbc1d359742f/websockets-17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2129c9a5fc05a230ce088745a790af073e582b584badc7b05db067ee326f56d7", size = 226241, upload-time = "2026-10-19T16:56:45.088Z" }, + { url = "https://files.pythonhosted.org/packages/06/28/16107b3b83fd2b5024bfecf8d000140f555204a0191202f9c4c981137810/websockets-17.3-cp314-cp314t-win32.whl", hash = "sha256:af7c474f0d8f4d2a196370e85033873c7b063a0b5da9a4f143aa023e3bd25407", size = 218365, upload-time = "2026-10-19T16:56:46.41Z" }, + { url = "https://files.pythonhosted.org/packages/77/dd/8b00e1dd552788d8c7762d7ef61de8f2f238d7357b1a02d4c4650b112196/websockets-17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c63ad657695e8f1a48624754cb6b464118ac50d4c1d477c26461c98390f0a62", size = 218706, upload-time = "2026-10-19T16:56:47.754Z" }, + { url = "https://files.pythonhosted.org/packages/39/eb/5d90d57a5cbe614b2f13433aaf1c0b068c804589379061ba06812a185a89/websockets-17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:3c4f631dc8d00e470cde3ec0a35d4b58a817bea592411956f1cf4099dbb306b3", size = 218622, upload-time = "2026-10-19T16:56:49.07Z" }, + { url = "https://files.pythonhosted.org/packages/6e/41/1efe5ecae1f32495dc75641704f959bd2d8e56cdc825f3acc5e010a51960/websockets-17.3-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:f300f28dff74ff4148c21e6971e4de6ca0c34e0086a3d95c017c5174ebf85ca5", size = 218211, upload-time = "2026-10-19T16:56:50.423Z" }, + { url = "https://files.pythonhosted.org/packages/01/ec/4edb653533241203db3f11522e122338d4fda50a41ce2a542029086f8eda/websockets-17.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:32029441af953e980b9940f042b337fe4ad9a97fa8b17a4ed878ec67b5b89ef4", size = 215944, upload-time = "2026-10-19T16:56:51.766Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b9/ec450b9650925dd5cf27c1f36d0464786fe3ac702ed656879c4c0dd5c4df/websockets-17.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:09f007b2150eff015b2109f006e2ac2012d376f41486defa88ae78a776b00cff", size = 216164, upload-time = "2026-10-19T16:56:53.125Z" }, + { url = "https://files.pythonhosted.org/packages/15/1e/63f7fbff9f0540a0331f1a62424c9ff24faa6ba8aec264f55e9e31017eea/websockets-17.3-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bdc658c9474864893e0b6b94f1390bf6a912952df1870dd93bb7d1a902b5628", size = 225608, upload-time = "2026-10-19T16:56:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1b/f6679ad61cb8c110033a1b30d561d5c1052983e202157251ff1778f27932/websockets-17.3-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:15decc0fe293d9e3f924bd483f3d76f3d0ff26845112a470e3f8842fec2c82a1", size = 226042, upload-time = "2026-10-19T16:56:55.727Z" }, + { url = "https://files.pythonhosted.org/packages/28/c6/ed993aaef8046cbbb939ae367ee3dd5383dbd055f0500a5b8aed101168b6/websockets-17.3-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:997617dd7856c5dad742bf6893b872319156dd43f47587541a59e4a852d52bbc", size = 227203, upload-time = "2026-10-19T16:56:57.076Z" }, + { url = "https://files.pythonhosted.org/packages/aa/96/7057dc71affe6c8252df8648f5a225642228a6fdd8d046a6b9aa1fb0e9eb/websockets-17.3-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a004c1fe5a72c496f28c6c1d8c2dc5cfe3278d99478e18f8b838c70a912b366f", size = 228900, upload-time = "2026-10-19T16:56:58.633Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/8b3268bb7e72b465e55172b86d55f58aa277fb6d9bac92aee615345874df/websockets-17.3-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35fe35d20baa661cef4a12ee7bc42eaf2376d43ef9816ab29dd47ca957f7a73a", size = 227684, upload-time = "2026-10-19T16:56:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/fa31c0a12240b1225c3712ff080d2f7ede4f695ca90e59fdb58e8affabba/websockets-17.3-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8b3bede7d583bc9f47d8693a29984882e59077db1a5238ad977fc70ae8169741", size = 226580, upload-time = "2026-10-19T16:57:01.371Z" }, + { url = "https://files.pythonhosted.org/packages/22/b5/75d911c206a3035e2ecfad107878e62c06e574b2437fa2cfb4ecef436fa3/websockets-17.3-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dafbb58a149d1ebd5767b3f064f911c5ee0897608005e51c3904b0ada2a8d99c", size = 223967, upload-time = "2026-10-19T16:57:02.839Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f8/f98aa36f611acd5770819f6690125c20d55f221fd6db8d00d284b5f78e89/websockets-17.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:80326bc571164bbbb6bead2e35d0c943b23b24fde629032197c58151529d24d8", size = 226689, upload-time = "2026-10-19T16:57:04.458Z" }, + { url = "https://files.pythonhosted.org/packages/1a/36/d52f5dff7597de6fbfc26c3d126abd668137915719aebf97abcc3be77eca/websockets-17.3-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:7d53fc4b90ef6e11de639dc18744d9be8cbd70ccb5ce80027aaffe7b1ab0215a", size = 225353, upload-time = "2026-10-19T16:57:05.857Z" }, + { url = "https://files.pythonhosted.org/packages/d4/10/dcd70ad9f9cd20e8af43284ac250af610e8bf9022da25f5b409153d033b4/websockets-17.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:fdd00937f15f5f93c8e53d8060a8c4c010431ab595a9b5b50ddd569a04603944", size = 225879, upload-time = "2026-10-19T16:57:07.249Z" }, + { url = "https://files.pythonhosted.org/packages/70/ab/f26c6bfa99d38ca3d1bf23145306d97c5d0c75e133a8d837373e4b3d4629/websockets-17.3-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1dae5cd6c92d4440b15f20aa2987baa73c47ea14baa0fec8536a542e82e0ab33", size = 227031, upload-time = "2026-10-19T16:57:08.817Z" }, + { url = "https://files.pythonhosted.org/packages/d7/3f/c8fd74cdb526cc65295b7468f2f84ba16678646d8174288822f99f510274/websockets-17.3-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:b225c656efa2dedecc81651d2290170af25887b7de4f38c5efc7c5ae2b9442ab", size = 224842, upload-time = "2026-10-19T16:57:10.298Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c5/b3a07ce1c54fc0f5441642f5352662b90d7e658c966730eaf6d78a5f20bc/websockets-17.3-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:bd645b5d2f810a181c44f4a71135e7c365b44f4be9ed3d1892635be13a857553", size = 225744, upload-time = "2026-10-19T16:57:11.615Z" }, + { url = "https://files.pythonhosted.org/packages/66/37/8321a8a6ab0215d4df0238e49ace5f539364cf337bb122b85c788e8c93ec/websockets-17.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:d2605a3b2af8d66f8b75312d223642c065ec773446c04566d9030ad5e2db13c9", size = 226006, upload-time = "2026-10-19T16:57:13.049Z" }, + { url = "https://files.pythonhosted.org/packages/f8/04/8c457a7e6a260b673bf4e6f2f422eb4af5f11c48505748359706f24f1bef/websockets-17.3-cp315-cp315-win32.whl", hash = "sha256:82e60c00cfb8493a5ba27cdf9b3695fdd153f666f73e11f18968f78ead1d8705", size = 218196, upload-time = "2026-10-19T16:57:14.41Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7c/c2df129d9e344b99bb388bcac54fbbdfeec29e6c716c12dc9a3398757940/websockets-17.3-cp315-cp315-win_amd64.whl", hash = "sha256:4d5f2492cb2192aab2d42e57f2caa2159b4fd20fde06f859ec13354f524b176c", size = 218490, upload-time = "2026-10-19T16:57:16.234Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4f/721b778c85559b2949e46246838e31a346dd815b3cdd8460fe517235675a/websockets-17.3-cp315-cp315-win_arm64.whl", hash = "sha256:55494475206113dbaf731dc8c5ffd5e1094175b753b7bbaad09fe3d7df61165c", size = 218403, upload-time = "2026-10-19T16:57:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/241d9ebb4dad79346b5a6abf9f14d7685b12ca358d250eea4920cdb1caf3/websockets-17.3-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:23cb6f6d64e3a389bb8ba150f947f9a6c8c0aca383e814ca3c5cffab738d5646", size = 218523, upload-time = "2026-10-19T16:57:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b0/bb/559fe539992102c66cbca38b488af6c7b184ab7736d819afc0f72268a2b4/websockets-17.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f27e7e8a85512b6a4b490d5756edde8e6772883a5800e1476172e44eda3f7d7c", size = 216103, upload-time = "2026-10-19T16:57:20.412Z" }, + { url = "https://files.pythonhosted.org/packages/34/ee/e8ade4f13a3d0da68636fe44bb8ac1a3e36fc57070512788320dead42067/websockets-17.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:6342f79d6f50e680c8aa59e0615d745e867e51ffd59c77093be60b7c433067af", size = 216316, upload-time = "2026-10-19T16:57:21.761Z" }, + { url = "https://files.pythonhosted.org/packages/4d/06/4812b2a7a5e6571d9be425e76cf54ee69aaa15720caa02307785aa5b19a7/websockets-17.3-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fc23e72bd7f8a8e540fed2244524d0a9aeb6ae3325f3d56f04a7a3debe5b04c2", size = 226467, upload-time = "2026-10-19T16:57:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e8/adfc05d7f9b6b0cce6cc554426c50b7058609119b6b84a39171fe5ed4e50/websockets-17.3-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:722ab747ff5dc28070b1a8c020dba6466f13f2ab56bc251ad301fefa2454aa1e", size = 226736, upload-time = "2026-10-19T16:57:24.419Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/d5b93dd933552554507edae9e3a4890269a8ea8b46019450ea9c8a30e7cc/websockets-17.3-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d20d6ea5aabbeba2e054d33e405192ef75dc12947bbd00ec97d23b14969d325", size = 228065, upload-time = "2026-10-19T16:57:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/10/37/642f0f1cf1c4814147a4f0586f796a0b38e42b69e5a296bfc270fb660051/websockets-17.3-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:90cc9973c51811918247a78d9ab2f54871296e4e37e69a98aba4d3c9acedbf62", size = 229692, upload-time = "2026-10-19T16:57:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/5b/33/eac81cf62ac394b61db6a27ccbe8fb9ae382016ef73ede1edf9242a6a43e/websockets-17.3-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63b87c93e28957e6dc2bb92d31727a863c9bf4a4daa080f0995fdcb0242c9166", size = 228503, upload-time = "2026-10-19T16:57:29.031Z" }, + { url = "https://files.pythonhosted.org/packages/0d/84/e057788a9bd056f4f7d3a06b59efaaf724bbc6f2d1e1d316f8a93c951abc/websockets-17.3-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f7431e71072962d3ed1768038200aa4f1e76d9c2d7bedd554d57a55faaf6644", size = 227379, upload-time = "2026-10-19T16:57:30.571Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a7/cca1f9f45b893bb5b7545f2b234f910e07fcd2f8065724ac1a62a0961be2/websockets-17.3-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:534ed583fd4323de45d196535a444cebd1af2105e6e4e0cdcbf8cea266a8747a", size = 224719, upload-time = "2026-10-19T16:57:31.895Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a0/5d07451027150c802ce4c9999e86994a3f0de5f4dbe3d1cce46995999269/websockets-17.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8879b6a4040ee925c638548238ab7fe612cf5fa8752fe847346da0da8be81c3f", size = 227564, upload-time = "2026-10-19T16:57:33.236Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/835f37e13fac0c615a700caae0d39deff1de2d2b9b341836a786f9b359e0/websockets-17.3-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:58e5d1e935a90878a935c7d2737a25c71cf4f1c13b6e6bef454ab722c4f338bb", size = 226218, upload-time = "2026-10-19T16:57:34.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/8a/718639939b947fe715501e8073350fd3f8a42f8b5eba3c0c2389c5836513/websockets-17.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:0a657b51e4a5f4ac577c11e706ff9a884ea5aaf3e2666bae283a251c63e66acb", size = 226712, upload-time = "2026-10-19T16:57:36.248Z" }, + { url = "https://files.pythonhosted.org/packages/e8/45/6bef8572b9b0dee51369b69776af1e4c7cc3a807f199dc3c165bbcf1199a/websockets-17.3-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:411aa0fb234f42a1444dea1e177890ddfc577b36ffdfafec25cb0d2c818e1af8", size = 227819, upload-time = "2026-10-19T16:57:37.586Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/3b726d0b46a132e641d2d8c0243ea8a7d39464dbaf28c7a206e51fbae681/websockets-17.3-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:623d4b44f64efdf235b1e8dce266057073d93cde6197e7d7684f20c297f67f30", size = 225573, upload-time = "2026-10-19T16:57:39.285Z" }, + { url = "https://files.pythonhosted.org/packages/90/96/cd972a12e1412d9b42c42ef42efbd81aa396ca9408d982d524012f011a8d/websockets-17.3-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:5d0c871efc696ef5eb8514df64b86ab28736db753ebb42a2101f3b562f4849c0", size = 226430, upload-time = "2026-10-19T16:57:40.915Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2d/e11524771588188a39f6da234779d4942496b96c5a10b92f3df30db48608/websockets-17.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1db2604e6aedb7a2cb2f6e0575d6c9a852ccac8239703a930ac83b63309177a5", size = 226728, upload-time = "2026-10-19T16:57:42.245Z" }, + { url = "https://files.pythonhosted.org/packages/17/9b/63722aed973f53ea579e1ce0c4563a2b8b536629e556feff26e4c6a2a60d/websockets-17.3-cp315-cp315t-win32.whl", hash = "sha256:07246d4f0293ef0b87eff3d7dd88c1f03dd9f678fa39f6de87b2af8504e7e0de", size = 218325, upload-time = "2026-10-19T16:57:43.585Z" }, + { url = "https://files.pythonhosted.org/packages/34/07/5e0c92e225546f1789c957c0383852797be7c899b07207e9b6eb4a93473f/websockets-17.3-cp315-cp315t-win_amd64.whl", hash = "sha256:856bbb24f2ee6436af7609267e73309e4bb0a0f9982f79f1c2e01019279c6671", size = 218641, upload-time = "2026-10-19T16:57:44.918Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e4/339663de65c44ab3eb5209890e86eed26e1a131d6afab3df6f1c3e5b4d8c/websockets-17.3-cp315-cp315t-win_arm64.whl", hash = "sha256:a22c4956a4d858200dba83f0691bb692d77b0eeddfca80157820d20b85dea4f1", size = 218564, upload-time = "2026-10-19T16:57:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1a/5582103e0362f86c4725051ded89e585ceaf750f26beace530b7e42c81c0/websockets-17.3-pp312-pypy312_pp80-macosx_10_15_x86_64.whl", hash = "sha256:0e65260a1b1d7b81651f47343d836de70b88b02358ae3658efa6382104ec37b1", size = 215872, upload-time = "2026-10-19T16:57:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a7/f69d65ed3ff9ae73d428a918300c23a992e4d50683090b8dc33cc3341d5a/websockets-17.3-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:94c02b0702656f083c098bdbd93bce22a7aa7a2b34ba56d2472efb67dab7cf58", size = 216136, upload-time = "2026-10-19T16:57:57.251Z" }, + { url = "https://files.pythonhosted.org/packages/69/76/c64fda4ac18a3aeb32e66acec481011a766add480b6362fad80e2c3c1963/websockets-17.3-pp312-pypy312_pp80-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ade76f8c84ade325bbed96806357fffd353fcc29a5166d6dc6d39381f6d9f76c", size = 217080, upload-time = "2026-10-19T16:57:58.593Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6d/085a3f0e6152bbe79917973f3d33cd008437de6c04675e2f15253b921f21/websockets-17.3-pp312-pypy312_pp80-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a215749c9a88ef2842e880215193e56ae60b856773c99702b078236bc6d4adb", size = 216966, upload-time = "2026-10-19T16:57:59.924Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b8/7eb904583ea2090b329f50487e6f8dc58eb558712b9f3c16f289f0be668e/websockets-17.3-pp312-pypy312_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7b5d23ef144b32645d97bd99f382c4394c443395f45fe1576786838d26e308", size = 217828, upload-time = "2026-10-19T16:58:01.822Z" }, + { url = "https://files.pythonhosted.org/packages/79/36/f54b4a1cd9456ca0a65a76d77d181621433793aca633dfeed214748ff4f0/websockets-17.3-pp312-pypy312_pp80-win_amd64.whl", hash = "sha256:13eace1b315345ebac5b27821f5dcce24e5cf9976ab18f79e00a59bdd67e5d19", size = 306301, upload-time = "2026-10-19T16:58:03.161Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b0/3f01fcb3406662f6deb1e3e72b34dd0979e993991c0b7435a9e12bc14f02/websockets-17.3-py3-none-any.whl", hash = "sha256:dc2b495a92b3971966bf102e73589f950db3ec1ca6713265355ce7771d97b199", size = 212375, upload-time = "2026-10-19T16:58:04.483Z" }, ]