-
Notifications
You must be signed in to change notification settings - Fork 312
Fix/mint buy example #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/mint buy example #141
Conversation
WalkthroughTwo pump fun protocol IDL files were updated to add new buy_exact instructions with corresponding error codes, while fee_config and fee_program accounts became required. TradeEvent and BuyEvent types gained ix_name fields. Learning examples were updated with placeholder addresses, and mint_and_buy.py added extend_account instruction support with track_volume parameter. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Program
participant ExtendAccount
participant BuyInstruction
User->>Program: Mint Token
Program-->>User: Bonding Curve Created
rect rgb(200, 220, 255)
Note over ExtendAccount: New Step
User->>ExtendAccount: extend_account(bonding_curve, user)
ExtendAccount-->>User: Account Extended
end
User->>BuyInstruction: buy(track_volume=True)
rect rgb(220, 255, 220)
Note over BuyInstruction: Enhanced with volume tracking
BuyInstruction->>Program: Execute with track_volume flag
Program-->>User: Purchase Complete
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes The changes span heterogeneous categories: IDL structure modifications (new instructions, error codes, event field additions) require careful validation of discriminators and account ordering, while learning example updates are repetitive placeholder replacements. The mint_and_buy.py logic addition is logic-dense but localized. Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
idl/pump_fun_idl.json (1)
4848-4855: IDL OptionBool encoding likely incompatible with codegenDefining OptionBool as a struct with a single bool omits the Option discriminant. Most Anchor-compatible IDLs represent this as {"option": "bool"} so client libraries encode [0] | [1, value].
Suggested shape:
{ "name": "OptionBool", "type": { "kind": "option", "option": "bool" } }If the program uses a custom struct instead of Option, document the exact wire format and keep clients manually encoding it (as you do). Otherwise, switch to the standard option form to avoid mis-serialization by generators.
🧹 Nitpick comments (11)
learning-examples/bonding-curve-progress/poll_bonding_curve_progress.py (1)
19-19: Read TOKEN_MINT from env; guard against placeholder/invalid keyUsing a placeholder string passes the non-empty check then fails on Pubkey.parse.
Apply this diff:
-TOKEN_MINT: Final[str] = "YOUR_TOKEN_MINT_ADDRESS_HERE" # Replace with actual token mint address +TOKEN_MINT: Final[str] = os.getenv("TOKEN_MINT") # Set in .envAnd before parsing:
if not RPC_URL or not TOKEN_MINT: print("❌ Set SOLANA_NODE_RPC_ENDPOINT and TOKEN_MINT in .env") return try: mint_pubkey: Pubkey = Pubkey.from_string(TOKEN_MINT) except Exception as e: print(f"❌ Invalid TOKEN_MINT: {e}") returnidl/pump_swap_idl.json (3)
3893-3902: Error messages apply to both buy variants; minor wording tweak
- Messages are prefixed with “buy: …” but errors also arise in buy_exact_quote_in. Consider neutral wording or “buy/buy_exact_quote_in: …”. Also prefer “cover fees” over “cover for fees.”
4010-4013: BuyEvent extension is useful; add explicit note on spendable vs max quote semantics
- Adding ix_name and min_base_amount_out is great. Please also clarify in docs whether BuyEvent.max_quote_amount_in carries spendable_quote_in for buy_exact_quote_in, or introduce a dedicated spendable_quote_in field to avoid ambiguity.
If you stick with one field, add a doc line like: “For buy_exact_quote_in, max_quote_amount_in equals spendable_quote_in.”
Also applies to: 4130-4136
708-1135: Clarify BuyEvent mapping for buy_exact_quote_in
- The
buy_exact_quote_ininstruction correctly includesfee_config/fee_program, unique discriminator, and args (spendable_quote_in,min_base_amount_out,track_volume).- The
BuyEventstruct now has bothmax_quote_amount_in(legacy) andmin_base_amount_out, plusix_name. To prevent client confusion:
- Update the event docs to state that for
ix_name == "buy_exact_quote_in",quote_amount_inrepresents thespendable_quote_inpassed in andmax_quote_amount_inis unused.- Clarify that consumers should branch on
ix_nameto interpret fields correctly.- (Optional) Consider adding a dedicated
spendable_quote_infield to the event.learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py (3)
549-555: Avoid hard-coded rent; fetch rent-exempt lamports at runtimeUsing a fixed 2_039_280 can drift and cause under/over-funding. Query rent for TokenAccount size (165) and add the swap amount.
- account_creation_lamports = 2_039_280 # Standard account creation cost - total_lamports = maximum_amount_in + account_creation_lamports + rent_lamports = await client.get_minimum_balance_for_rent_exemption(165) + total_lamports = maximum_amount_in + rent_lamports
676-687: Add simple retries for submission/confirmationNetwork hiccups are common. Wrap send/confirm with 3 attempts and exponential backoff.
- result = await client.send_transaction( - transaction, - opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed), - ) - - tx_signature = result.value + tx_signature = None + for attempt in range(3): + try: + result = await client.send_transaction( + transaction, + opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed), + ) + tx_signature = result.value + await client.confirm_transaction(tx_signature, commitment="confirmed") + break + except Exception as e: + await asyncio.sleep(0.5 * (2 ** attempt)) + if tx_signature is None: + print("Send/confirm retries exhausted.") + return None - print(f"Transaction sent: https://solscan.io/tx/{tx_signature}") - - print("Waiting for confirmation...") - await client.confirm_transaction(tx_signature, commitment="confirmed") - print("Transaction confirmed!") + print(f"Transaction sent and confirmed: https://solscan.io/tx/{tx_signature}")
1-739: Optional: switch prints to centralized logger and keep lines <= 88 charsExamples should still follow repo logging and formatting guidelines. Consider using get_logger(name) and logging.exception() in except blocks; also wrap long f-strings to respect 88-char limit.
learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py (4)
516-523: Guard against None from minimum amount calcIf calculation fails and returns None, struct packing will throw. Add a check and abort early.
minimum_amount_out = calculate_minimum_amount_out_from_pool_state( pool_state_data, amount_in, slippage_tolerance ) + if minimum_amount_out is None: + print("Failed to calculate minimum_amount_out") + return None
539-541: Compute rent-exempt amount instead of hard-codingSame concern as exact-out. Query rent, then add amount_in.
- account_creation_lamports = 2_039_280 # Standard account creation cost - total_lamports = amount_in + account_creation_lamports + rent_lamports = await client.get_minimum_balance_for_rent_exemption(165) + total_lamports = amount_in + rent_lamports
665-677: Add retries around send/confirmImprove resilience with a small retry loop.
- result = await client.send_transaction( - transaction, - opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed), - ) - - tx_signature = result.value + tx_signature = None + for attempt in range(3): + try: + result = await client.send_transaction( + transaction, + opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed), + ) + tx_signature = result.value + await client.confirm_transaction(tx_signature, commitment="confirmed") + break + except Exception: + await asyncio.sleep(0.5 * (2 ** attempt)) + if tx_signature is None: + print("Send/confirm retries exhausted.") + return None
1-734: Optional: adopt repo logger and clamp slippage defaults
- Use src.utils.logger.get_logger(name) and logging.exception() per guidelines.
- Consider default SLIPPAGE_TOLERANCE of 0.01–0.05 and clamp user-provided values into [0, 0.5] for safety.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
idl/pump_fun_idl.json(4 hunks)idl/pump_swap_idl.json(4 hunks)learning-examples/bonding-curve-progress/poll_bonding_curve_progress.py(1 hunks)learning-examples/fetch_price.py(1 hunks)learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py(1 hunks)learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py(1 hunks)learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py(1 hunks)learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py(1 hunks)learning-examples/mint_and_buy.py(6 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Enforce max line length of 88 characters in Python (Ruff config)
Use 4 spaces for indentation in Python files
Target Python version 3.11+ features and syntax
Prefer double quotes for strings in Python code
Enable and honor import sorting in Python modules
Apply Ruff rule sets: Security (S), Annotations (ANN), Exceptions (BLE, TRY), Complexity (C90), Pylint conventions (PL), and no commented-out code (ERA)
Order imports as: standard library, third-party, then local imports
Use Google-style docstrings for functions and classes
Provide type hints for all public functions
Use a module-level logger via get_logger(name)
Wrap risky operations in try/except with proper logging
Implement rate limiting and retry mechanisms where external calls are made
Perform comprehensive input validation for externally sourced data
**/*.py: Limit lines to 88 characters
Use 4 spaces for indentation (never tabs)
Use double quotes for strings consistently
Target Python 3.11+ features and syntax
Enable automatic import sorting and organization
Order imports: standard library first, third-party second, local last
Add type hints to all public functions and methods
Use modern typing syntax (e.g., X | Y unions)
Include explicit return type annotations
Use typing.Any for complex/unknown types (from typing import Any)
Use Google-style docstrings for all functions and classes
Wrap external operations (RPC calls, file I/O) in try/except blocks
Log exceptions with context using logging.exception()
Provide meaningful error messages when handling exceptions
Do not suppress exceptions without good reason
Use get_logger(name) with logger from utils.logger
Use appropriate log levels (DEBUG, INFO, WARNING, ERROR) with contextual messages
Never hardcode secrets (private keys, API tokens)
Use environment variables for sensitive configuration
Do not log sensitive information
Validate all external inputs
Comply with Ruff security rules (S)
Comply with Ruff type annotation rules (ANN)
Comply...
Files:
learning-examples/fetch_price.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_out.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_in.pylearning-examples/mint_and_buy.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_out.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_in.pylearning-examples/bonding-curve-progress/poll_bonding_curve_progress.py
learning-examples/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Treat learning-examples Python scripts as integration tests for manual runs
learning-examples/**/*.py: Use learning examples to test new features before integration
Use learning examples to validate platform-specific functionality
Use learning examples for performance benchmarking
Use learning examples for educational purposes for new developersAlways test changes using scripts in learning-examples/ before modifying the main bot
Files:
learning-examples/fetch_price.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_out.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_in.pylearning-examples/mint_and_buy.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_out.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_in.pylearning-examples/bonding-curve-progress/poll_bonding_curve_progress.py
learning-examples/**
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
Place integration tests and validation scenarios under learning-examples/
Files:
learning-examples/fetch_price.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_out.pylearning-examples/letsbonk-buy-sell/manual_sell_exact_in.pylearning-examples/mint_and_buy.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_out.pylearning-examples/letsbonk-buy-sell/manual_buy_exact_in.pylearning-examples/bonding-curve-progress/poll_bonding_curve_progress.py
learning-examples/fetch_price.py
📄 CodeRabbit inference engine (AGENTS.md)
Use
uv run learning-examples/fetch_price.pyfor unit-level testing of price fetching
Files:
learning-examples/fetch_price.py
🧠 Learnings (1)
📚 Learning: 2025-10-04T12:42:50.785Z
Learnt from: CR
PR: chainstacklabs/pumpfun-bonkfun-bot#0
File: AGENTS.md:0-0
Timestamp: 2025-10-04T12:42:50.785Z
Learning: Applies to learning-examples/manual_{buy,sell}.py : Test changes using manual_buy.py and manual_sell.py with minimal amounts before production use
Applied to files:
learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py
🧬 Code graph analysis (1)
learning-examples/mint_and_buy.py (1)
src/core/wallet.py (1)
pubkey(24-26)
🪛 Ruff (0.14.1)
learning-examples/bonding-curve-progress/poll_bonding_curve_progress.py
19-19: Possible hardcoded password assigned to: "TOKEN_MINT"
(S105)
🔇 Additional comments (7)
idl/pump_fun_idl.json (2)
827-1242: New instruction buy_exact_sol_in: accounts/args look consistentAccounts mirror buy with fee_config/fee_program now required; args include spendable_sol_in, min_tokens_out, track_volume. LGTM. Please ensure clients pass fee_config and fee_program everywhere.
Confirm on-chain expects the exact accounts/order shown here and that rent/fee error codes (6040–6041) are surfaced by your clients.
5003-5096: TradeEvent adds ix_name: good for telemetryNo issues spotted. Ensure downstream decoders account for the extra field.
learning-examples/mint_and_buy.py (3)
73-75: Discriminator matches IDLEXTEND_ACCOUNT_DISCRIMINATOR bytes align with IDL. LGTM.
230-231: track_volume Option encoding aligns with wire formatEncoding [1, 0|1] for Some(false|true) is consistent with Option. Keep IDL and docs in sync.
Add a brief note in README about OptionBool wire format to avoid client drift.
Also applies to: 268-271, 276-277
348-353: Extend-account placement looks correctExtending bonding_curve right after create and before buy makes sense. LGTM.
learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py (1)
42-44: Swapping hard-coded mint for a placeholder is correctGood change. Script will now fail-fast with ValueError until users set a real mint, which is desirable for examples.
Before merging, run the example with a real mint to ensure no regressions.
learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py (1)
42-44: Placeholder mint address: good safety defaultAppropriate for examples; it forces users to supply a real mint.
Smoke-test with a real mint after merge.
| LAMPORTS_PER_SOL: Final[int] = 1_000_000_000 | ||
| TOKEN_DECIMALS: Final[int] = 6 | ||
| CURVE_ADDRESS: Final[str] = "6GXfUqrmPM4VdN1NoDZsE155jzRegJngZRjMkGyby7do" | ||
| CURVE_ADDRESS: Final[str] = "YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Load CURVE_ADDRESS from env and validate
Avoid hardcoded placeholder; read from env and fail fast if unset/placeholder.
Apply this diff:
-CURVE_ADDRESS: Final[str] = "YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address
+CURVE_ADDRESS: Final[str] = os.environ.get(
+ "CURVE_ADDRESS", "YOUR_BONDING_CURVE_ADDRESS_HERE"
+)Add early check:
if not CURVE_ADDRESS or CURVE_ADDRESS.endswith("_HERE"):
raise ValueError("Set CURVE_ADDRESS env to a valid bonding curve address")Run as per guidelines:
🏁 Script executed:
uv run learning-examples/fetch_price.pyLength of output: 40
🏁 Script executed:
#!/bin/bash
sed -n '1,200p' learning-examples/fetch_price.pyLength of output: 2544
Load CURVE_ADDRESS from env and validate
Replace the hardcoded placeholder at learning-examples/fetch_price.py:15 with an environment lookup and fail fast if it’s unset or still using the placeholder:
import os
from typing import Final
-CURVE_ADDRESS: Final[str] = "YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address
+CURVE_ADDRESS: Final[str] = os.environ.get(
+ "CURVE_ADDRESS", "YOUR_BONDING_CURVE_ADDRESS_HERE"
+)
+
+if not CURVE_ADDRESS or CURVE_ADDRESS.endswith("_HERE"):
+ raise ValueError("Set CURVE_ADDRESS env to a valid bonding curve address")Then verify the change by running:
python learning-examples/fetch_price.py🤖 Prompt for AI Agents
In learning-examples/fetch_price.py around line 15, the CURVE_ADDRESS is
hardcoded to a placeholder; change it to read from an environment variable
(e.g., os.environ or os.getenv) and validate it immediately: if the env var is
missing, empty, or still equals the placeholder string, fail fast with a clear
error (raise SystemExit or log an error and exit with non-zero status) so the
script stops early; update any import (os, sys or logging) as needed and then
run python learning-examples/fetch_price.py to verify the guard triggers when
not set.
| TOKEN_MINT_ADDRESS = Pubkey.from_string( | ||
| "MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot" | ||
| "YOUR_TOKEN_MINT_ADDRESS_HERE" | ||
| ) # Replace with actual token mint address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import-time crash: lazily parse TOKEN_MINT from env
Same issue as the other script. Don’t call Pubkey.from_string at module import.
Apply this diff here:
-TOKEN_MINT_ADDRESS = Pubkey.from_string(
- "YOUR_TOKEN_MINT_ADDRESS_HERE"
-) # Replace with actual token mint address
+TOKEN_MINT_STR = os.environ.get("TOKEN_MINT", "YOUR_TOKEN_MINT_ADDRESS_HERE")And in main():
try:
token_mint = Pubkey.from_string(TOKEN_MINT_STR)
except ValueError as e:
print(f"Invalid TOKEN_MINT: {e}")
sys.exit(1)
# replace TOKEN_MINT_ADDRESS usages with token_mintBased on learnings.
🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py around lines
42-44, avoid calling Pubkey.from_string at module import which can crash;
instead store the mint as a raw string constant (e.g. TOKEN_MINT_STR = "..." or
read from env) and parse it lazily inside main() with a try/except that catches
ValueError, prints an error and exits; update all uses of TOKEN_MINT_ADDRESS to
use the parsed token_mint variable from main() and ensure sys.exit(1) is invoked
on parse failure.
| TOKEN_MINT_ADDRESS = Pubkey.from_string( | ||
| "MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot" | ||
| "YOUR_TOKEN_MINT_ADDRESS_HERE" | ||
| ) # Replace with actual token mint address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import-time crash: lazily parse TOKEN_MINT from env
Calling Pubkey.from_string on a placeholder raises ValueError at import time; the script never reaches main(). Parse lazily and validate.
Apply this diff here:
-TOKEN_MINT_ADDRESS = Pubkey.from_string(
- "YOUR_TOKEN_MINT_ADDRESS_HERE"
-) # Replace with actual token mint address
+TOKEN_MINT_STR = os.environ.get("TOKEN_MINT", "YOUR_TOKEN_MINT_ADDRESS_HERE")Then inside main(), parse and use a local variable:
# inside main(), before first use
try:
token_mint = Pubkey.from_string(TOKEN_MINT_STR)
except ValueError as e:
print(f"Invalid TOKEN_MINT: {e}")
sys.exit(1)
# replace all TOKEN_MINT_ADDRESS usages with token_mint🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py around lines
42-44, avoid calling Pubkey.from_string at import time which raises ValueError
for placeholder; change the top-level constant to a plain string (e.g.
TOKEN_MINT_STR = "YOUR_TOKEN_MINT_ADDRESS_HERE") and remove Pubkey.from_string
there, then inside main() before first use attempt to parse it with try:
token_mint = Pubkey.from_string(TOKEN_MINT_STR) except ValueError as e:
print(f"Invalid TOKEN_MINT: {e}") and call sys.exit(1); replace all usages of
TOKEN_MINT_ADDRESS with the local token_mint variable and ensure sys is imported
if not already.
| + encode_pubkey(creator) | ||
| ) | ||
|
|
||
| return Instruction(PUMP_PROGRAM, data, accounts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Instruction() arguments are in wrong order
solders.instruction.Instruction expects (program_id, accounts, data). Code passes (program_id, data, accounts), which will corrupt the instruction and fail at runtime.
Apply this diff:
- return Instruction(PUMP_PROGRAM, data, accounts)
+ return Instruction(PUMP_PROGRAM, accounts, data)Repeat the same fix for:
- Line 197 in create_pump_create_instruction
- Line 216 in create_extend_account_instruction
- Line 279 in create_buy_instruction
Also applies to: 216-217, 279-279
🤖 Prompt for AI Agents
In learning-examples/mint_and_buy.py at lines 197, 216-217, and 279, the
Instruction() constructor arguments are passed in the wrong order (program_id,
data, accounts) but solders.instruction.Instruction expects (program_id,
accounts, data); update each return to call Instruction(PUMP_PROGRAM, accounts,
data) (or the corresponding program_id variable for that function) so accounts
comes second and data third for create_pump_create_instruction (line 197),
create_extend_account_instruction (lines 216-217), and create_buy_instruction
(line 279).
#138
Summary by CodeRabbit
New Features
Improvements
Chores