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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
442 changes: 438 additions & 4 deletions idl/pump_fun_idl.json

Large diffs are not rendered by default.

453 changes: 449 additions & 4 deletions idl/pump_swap_idl.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

# Constants
RPC_URL: Final[str] = os.getenv("SOLANA_NODE_RPC_ENDPOINT")
TOKEN_MINT: Final[str] = "xWrzYY4c1LnbSkLrd2LDUg9vw7YtVyJhGmw7MABpump"
TOKEN_MINT: Final[str] = "YOUR_TOKEN_MINT_ADDRESS_HERE" # Replace with actual token mint address
PUMP_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string(
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
)
Expand Down
2 changes: 1 addition & 1 deletion learning-examples/fetch_price.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.py

Length of output: 40


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' learning-examples/fetch_price.py

Length 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.


# Here and later all the discriminators are precalculated. See learning-examples/calculate_discriminator.py
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
Expand Down
2 changes: 1 addition & 1 deletion learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
load_dotenv()

TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address

# Configuration constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
load_dotenv()

TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address

# Configuration constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
load_dotenv()

TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
Comment on lines 42 to 44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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_mint

Based 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.


# Configuration constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
load_dotenv()

TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
Comment on lines 42 to 44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.


# Configuration constants
Expand Down
34 changes: 34 additions & 0 deletions learning-examples/mint_and_buy.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
# Discriminators
CREATE_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 8576854823835016728)
BUY_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 16927863322537952870)
EXTEND_ACCOUNT_DISCRIMINATOR: Final[bytes] = bytes(
[234, 102, 194, 203, 150, 72, 62, 229]
)

# From environment
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
Expand Down Expand Up @@ -194,6 +197,25 @@ def encode_pubkey(pubkey: Pubkey) -> bytes:
return Instruction(PUMP_PROGRAM, data, accounts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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).



def create_extend_account_instruction(
bonding_curve: Pubkey,
user: Pubkey,
) -> Instruction:
"""Create the extend_account instruction to expand bonding curve account size."""
accounts = [
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=user, is_signer=True, 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),
]

# No arguments for extend_account instruction
data = EXTEND_ACCOUNT_DISCRIMINATOR

return Instruction(PUMP_PROGRAM, data, accounts)


def create_buy_instruction(
global_state: Pubkey,
fee_recipient: Pubkey,
Expand All @@ -205,6 +227,7 @@ def create_buy_instruction(
creator_vault: Pubkey,
token_amount: int,
max_sol_cost: int,
track_volume: bool = True,
) -> Instruction:
"""Create the buy instruction."""
accounts = [
Expand Down Expand Up @@ -242,10 +265,15 @@ def create_buy_instruction(
),
]

# Encode OptionBool for track_volume
# OptionBool: [0] = None, [1, 0] = Some(false), [1, 1] = Some(true)
track_volume_bytes = bytes([1, 1 if track_volume else 0])

data = (
BUY_DISCRIMINATOR
+ struct.pack("<Q", token_amount)
+ struct.pack("<Q", max_sol_cost)
+ track_volume_bytes
)

return Instruction(PUMP_PROGRAM, data, accounts)
Expand Down Expand Up @@ -317,6 +345,11 @@ async def main():
symbol=TOKEN_SYMBOL,
uri=TOKEN_URI,
),
# Extend bonding curve account (required for frontend visibility)
create_extend_account_instruction(
bonding_curve=bonding_curve,
user=payer.pubkey(),
),
# Create user ATA
create_idempotent_associated_token_account(
payer.pubkey(),
Expand All @@ -336,6 +369,7 @@ async def main():
creator_vault=creator_vault,
token_amount=expected_tokens,
max_sol_cost=max_sol_cost,
track_volume=True,
),
]

Expand Down