From 4606a07d5d5c7478debae31af0f9e617d5294f37 Mon Sep 17 00:00:00 2001 From: smypmsa Date: Fri, 20 Mar 2026 18:03:06 +0000 Subject: [PATCH 1/2] fix: align instruction builders with IDL signer/writable flags and data encoding Audited all instruction builders against pump_fun_idl.json and pump_fun_amm_idl.json. Fixed 8 discrepancies: OptionBool encoding (1 byte not 2), removed spurious track_volume from bonding curve buy/sell, corrected writable/signer flags on create_v2 and extend_account, fixed claim_cashback signer flag, and added missing PUMP_AMM_PROGRAM account to collect_coin_creator_fee. E2e mainnet tests confirm PumpSwap buy+sell, bonding curve buy+sell, and token launch all work correctly. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/mainnet-test.sh | 134 ++++++++++++++++-- src/pumpfun_cli/protocol/instructions.py | 25 ++-- .../test_protocol/test_extras_instructions.py | 20 ++- tests/test_protocol/test_instructions.py | 63 ++++++++ .../test_pumpswap_instructions.py | 38 +++++ tests/test_protocol/test_token_types.py | 4 +- 6 files changed, 254 insertions(+), 30 deletions(-) diff --git a/scripts/mainnet-test.sh b/scripts/mainnet-test.sh index 921b04c..f6d44de 100755 --- a/scripts/mainnet-test.sh +++ b/scripts/mainnet-test.sh @@ -221,10 +221,17 @@ else: GRADUATED_MINT=$(echo "$TRENDING" | python3 -c " import json, sys tokens = json.load(sys.stdin) +# Prefer graduated tokens with higher SOL reserves — pools with extreme +# base/quote ratios can trigger on-chain overflow at buy.rs:400. +best = None for t in tokens: if t.get('complete') and t.get('pump_swap_pool'): - print(t['mint']) - break + if best is None: + best = t + elif t.get('market_cap', 0) > best.get('market_cap', 0): + best = t +if best: + print(best['mint']) else: print('') " 2>/dev/null) @@ -259,14 +266,14 @@ if [[ "$SKIP_TRADING" == true ]]; then else echo "=== Group 5: Bonding Curve Trading ===" if [[ -n "$ACTIVE_MINT" ]]; then - BUY_OUT=$(uv run pumpfun buy "$ACTIVE_MINT" 0.001 --confirm 2>&1) + BUY_OUT=$(uv run pumpfun buy "$ACTIVE_MINT" 0.001 --slippage 50 --confirm 2>&1) BUY_EXIT=$? if [[ $BUY_EXIT -eq 0 ]]; then record "BC Trade" "buy 0.001 --confirm" "PASS" echo " Buy OK. Waiting 5s for RPC state..." sleep 5 - SELL_OUT=$(uv run pumpfun sell "$ACTIVE_MINT" all --confirm 2>&1) + SELL_OUT=$(uv run pumpfun sell "$ACTIVE_MINT" all --slippage 50 --confirm 2>&1) SELL_EXIT=$? if [[ $SELL_EXIT -eq 0 ]]; then record "BC Trade" "sell all --confirm" "PASS" @@ -274,7 +281,7 @@ else # Retry once after delay echo " Sell failed, retrying in 5s..." sleep 5 - SELL_OUT=$(uv run pumpfun sell "$ACTIVE_MINT" all --confirm 2>&1) + SELL_OUT=$(uv run pumpfun sell "$ACTIVE_MINT" all --slippage 50 --confirm 2>&1) SELL_EXIT=$? if [[ $SELL_EXIT -eq 0 ]]; then record "BC Trade" "sell all --confirm" "PASS" "Needed retry (RPC lag)" @@ -297,20 +304,121 @@ else echo "=== Group 6: PumpSwap AMM Trading ===" if [[ -n "$GRADUATED_MINT" ]]; then - AMM_OUT=$(uv run pumpfun buy "$GRADUATED_MINT" 0.001 --force-amm --confirm 2>&1) - AMM_EXIT=$? - if [[ $AMM_EXIT -eq 0 ]]; then - record "PumpSwap" "buy --force-amm --confirm" "PASS" "BUG-1 may be fixed!" - elif echo "$AMM_OUT" | grep -q "6023"; then - record "PumpSwap" "buy --force-amm --confirm" "ISSUE" "Error 6023 — known BUG-1" - else - record "PumpSwap" "buy --force-amm --confirm" "FAIL" "$(echo "$AMM_OUT" | head -1 | cut -c1-60)" + # Build a list of graduated mints to try (some pools have on-chain overflow bugs) + ALL_GRADUATED=$(echo "$TRENDING" | python3 -c " +import json, sys +tokens = json.load(sys.stdin) +for t in tokens: + if t.get('complete') and t.get('pump_swap_pool'): + print(t['mint']) +" 2>/dev/null) + + AMM_BUY_OK=false + AMM_MINT="" + while IFS= read -r CANDIDATE; do + [[ -z "$CANDIDATE" ]] && continue + AMM_OUT=$(uv run pumpfun buy "$CANDIDATE" 0.001 --slippage 50 --force-amm --confirm 2>&1) + AMM_EXIT=$? + if [[ $AMM_EXIT -eq 0 ]]; then + AMM_BUY_OK=true + AMM_MINT="$CANDIDATE" + break + elif echo "$AMM_OUT" | grep -q "6023"; then + echo " Pool overflow (6023) on ${CANDIDATE:0:12}…, trying next" + continue + else + # Non-overflow failure — record and stop + record "PumpSwap" "buy --force-amm --confirm" "FAIL" "$(echo "$AMM_OUT" | head -1 | cut -c1-60)" + break + fi + done <<< "$ALL_GRADUATED" + + if [[ "$AMM_BUY_OK" == "true" ]]; then + record "PumpSwap" "buy --force-amm --confirm" "PASS" + echo " PumpSwap buy OK. Waiting 5s for RPC state..." + sleep 5 + + AMM_SELL_OUT=$(uv run pumpfun sell "$AMM_MINT" all --slippage 50 --confirm 2>&1) + AMM_SELL_EXIT=$? + if [[ $AMM_SELL_EXIT -eq 0 ]]; then + record "PumpSwap" "sell all --confirm" "PASS" + else + echo " PumpSwap sell failed, retrying in 5s..." + sleep 5 + AMM_SELL_OUT=$(uv run pumpfun sell "$AMM_MINT" all --slippage 50 --confirm 2>&1) + AMM_SELL_EXIT=$? + if [[ $AMM_SELL_EXIT -eq 0 ]]; then + record "PumpSwap" "sell all --confirm" "PASS" "Needed retry (RPC lag)" + else + record "PumpSwap" "sell all --confirm" "FAIL" "Failed after retry" + fi + fi + elif [[ "$AMM_BUY_OK" == "false" ]] && [[ -z "$AMM_MINT" ]]; then + record "PumpSwap" "buy --force-amm --confirm" "ISSUE" "All graduated pools hit error 6023" fi else record "PumpSwap" "buy --force-amm" "ISSUE" "No graduated mint found" fi echo " Done." + # --- 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_EXIT=$? + echo "$LAUNCH_OUT" > "$LAST_OUTPUT_FILE" + if [[ $LAUNCH_EXIT -eq 0 ]]; then + record "Launch" "launch" "PASS" + LAUNCHED_MINT=$(echo "$LAUNCH_OUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('mint',''))" 2>/dev/null) + echo " Launched: $LAUNCHED_MINT" + else + LAUNCH_ERR=$(echo "$LAUNCH_OUT" | grep -m1 "Error:" | cut -c1-60) + [[ -z "$LAUNCH_ERR" ]] && LAUNCH_ERR="exit=$LAUNCH_EXIT" + record "Launch" "launch" "FAIL" "$LAUNCH_ERR" + LAUNCHED_MINT="" + 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_EXIT=$? + echo "$LAUNCH_BUY_OUT" > "$LAST_OUTPUT_FILE" + if [[ $LAUNCH_BUY_EXIT -eq 0 ]]; then + record "Launch" "launch --buy 0.001" "PASS" + LAUNCHED_BUY_MINT=$(echo "$LAUNCH_BUY_OUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('mint',''))" 2>/dev/null) + echo " Launched+bought: $LAUNCHED_BUY_MINT" + + # Sell from the launched token to test full cycle + if [[ -n "$LAUNCHED_BUY_MINT" ]]; then + echo " Waiting 5s for RPC state..." + sleep 5 + LSELL_OUT=$(uv run pumpfun sell "$LAUNCHED_BUY_MINT" all --slippage 50 --confirm 2>&1) + LSELL_EXIT=$? + if [[ $LSELL_EXIT -eq 0 ]]; then + record "Launch" "sell launched token" "PASS" + else + sleep 5 + LSELL_OUT=$(uv run pumpfun sell "$LAUNCHED_BUY_MINT" all --slippage 50 --confirm 2>&1) + LSELL_EXIT=$? + if [[ $LSELL_EXIT -eq 0 ]]; then + record "Launch" "sell launched token" "PASS" "Needed retry" + else + record "Launch" "sell launched token" "FAIL" "Failed after retry" + fi + fi + fi + else + LAUNCH_BUY_ERR=$(echo "$LAUNCH_BUY_OUT" | grep -m1 "Error:" | cut -c1-60) + [[ -z "$LAUNCH_BUY_ERR" ]] && LAUNCH_BUY_ERR="exit=$LAUNCH_BUY_EXIT" + record "Launch" "launch --buy 0.001" "FAIL" "$LAUNCH_BUY_ERR" + fi + rm -f /tmp/e2e_test_token.png + echo " Done." + # --- Group 7: Extras --- echo "=== Group 7: Extras ===" diff --git a/src/pumpfun_cli/protocol/instructions.py b/src/pumpfun_cli/protocol/instructions.py index 35b0606..156d7cf 100644 --- a/src/pumpfun_cli/protocol/instructions.py +++ b/src/pumpfun_cli/protocol/instructions.py @@ -39,8 +39,9 @@ ) from pumpfun_cli.protocol.idl_parser import IDLParser -# Trailing bytes appended after the two u64 args (track_volume flag). -_TRACK_VOLUME = bytes([1, 1]) +# OptionBool(true) = 1 byte: the IDL defines OptionBool as struct { bool }, +# so it serialises to a single 0x01 byte, NOT 2 bytes (Option). +_TRACK_VOLUME = bytes([1]) def build_buy_instructions( @@ -103,10 +104,7 @@ def build_buy_instructions( discriminators = idl.get_instruction_discriminators() instruction_data = ( - discriminators["buy"] - + struct.pack(" Date: Fri, 20 Mar 2026 18:35:48 +0000 Subject: [PATCH 2/2] fix: address CodeRabbit review comments on PR #18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - extend_account: fix user account is_signer=True → False per IDL; update matching test assertion to expect is_signer=False - mainnet-test.sh: introduce AMM_OVERFLOW_ONLY flag so non-6023 AMM failures are not misclassified as "All graduated pools hit error 6023" - mainnet-test.sh: validate parsed mint from --json launch output before recording PASS; fall back to FAIL with "missing mint" message for both launch and launch --buy branches Co-Authored-By: Claude Sonnet 4.6 --- scripts/mainnet-test.sh | 22 +++++++++++++++++----- src/pumpfun_cli/protocol/instructions.py | 2 +- tests/test_protocol/test_instructions.py | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/scripts/mainnet-test.sh b/scripts/mainnet-test.sh index f6d44de..55022f3 100755 --- a/scripts/mainnet-test.sh +++ b/scripts/mainnet-test.sh @@ -315,6 +315,7 @@ for t in tokens: AMM_BUY_OK=false AMM_MINT="" + AMM_OVERFLOW_ONLY=true while IFS= read -r CANDIDATE; do [[ -z "$CANDIDATE" ]] && continue AMM_OUT=$(uv run pumpfun buy "$CANDIDATE" 0.001 --slippage 50 --force-amm --confirm 2>&1) @@ -328,6 +329,7 @@ for t in tokens: continue else # Non-overflow failure — record and stop + AMM_OVERFLOW_ONLY=false record "PumpSwap" "buy --force-amm --confirm" "FAIL" "$(echo "$AMM_OUT" | head -1 | cut -c1-60)" break fi @@ -353,7 +355,7 @@ for t in tokens: record "PumpSwap" "sell all --confirm" "FAIL" "Failed after retry" fi fi - elif [[ "$AMM_BUY_OK" == "false" ]] && [[ -z "$AMM_MINT" ]]; then + elif [[ "$AMM_OVERFLOW_ONLY" == "true" ]]; then record "PumpSwap" "buy --force-amm --confirm" "ISSUE" "All graduated pools hit error 6023" fi else @@ -373,9 +375,14 @@ for t in tokens: LAUNCH_EXIT=$? echo "$LAUNCH_OUT" > "$LAST_OUTPUT_FILE" if [[ $LAUNCH_EXIT -eq 0 ]]; then - record "Launch" "launch" "PASS" LAUNCHED_MINT=$(echo "$LAUNCH_OUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('mint',''))" 2>/dev/null) - echo " Launched: $LAUNCHED_MINT" + if [[ -n "$LAUNCHED_MINT" ]]; then + record "Launch" "launch" "PASS" + echo " Launched: $LAUNCHED_MINT" + else + record "Launch" "launch" "FAIL" "Invalid --json payload (missing mint)" + LAUNCHED_MINT="" + fi else LAUNCH_ERR=$(echo "$LAUNCH_OUT" | grep -m1 "Error:" | cut -c1-60) [[ -z "$LAUNCH_ERR" ]] && LAUNCH_ERR="exit=$LAUNCH_EXIT" @@ -388,9 +395,14 @@ for t in tokens: LAUNCH_BUY_EXIT=$? echo "$LAUNCH_BUY_OUT" > "$LAST_OUTPUT_FILE" if [[ $LAUNCH_BUY_EXIT -eq 0 ]]; then - record "Launch" "launch --buy 0.001" "PASS" LAUNCHED_BUY_MINT=$(echo "$LAUNCH_BUY_OUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('mint',''))" 2>/dev/null) - echo " Launched+bought: $LAUNCHED_BUY_MINT" + if [[ -n "$LAUNCHED_BUY_MINT" ]]; then + record "Launch" "launch --buy 0.001" "PASS" + echo " Launched+bought: $LAUNCHED_BUY_MINT" + else + record "Launch" "launch --buy 0.001" "FAIL" "Invalid --json payload (missing mint)" + LAUNCHED_BUY_MINT="" + fi # Sell from the launched token to test full cycle if [[ -n "$LAUNCHED_BUY_MINT" ]]; then diff --git a/src/pumpfun_cli/protocol/instructions.py b/src/pumpfun_cli/protocol/instructions.py index 156d7cf..597e720 100644 --- a/src/pumpfun_cli/protocol/instructions.py +++ b/src/pumpfun_cli/protocol/instructions.py @@ -365,7 +365,7 @@ def build_extend_account_instruction( """ accounts = [ AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True), - AccountMeta(pubkey=user, is_signer=True, is_writable=False), + 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), diff --git a/tests/test_protocol/test_instructions.py b/tests/test_protocol/test_instructions.py index 1aeb173..7d3e713 100644 --- a/tests/test_protocol/test_instructions.py +++ b/tests/test_protocol/test_instructions.py @@ -222,9 +222,9 @@ def test_create_v2_mayhem_program_is_writable(): def test_extend_account_user_not_writable(): - """extend_account account[1] (user) must be is_writable=False, is_signer=True per IDL.""" + """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 True + assert ix.accounts[1].is_signer is False assert ix.accounts[1].is_writable is False