Skip to content

Conversation

@smypmsa
Copy link
Member

@smypmsa smypmsa commented Oct 21, 2025

Summary by CodeRabbit

  • New Features
    • Updated program interface (v0.2.0) with enhanced platform configuration management.
    • Added support for creator fees and platform curve parameter management.
    • Introduced CP Swap migration capabilities and Token 2022 support.
    • Added comprehensive reference implementations demonstrating trading operations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

Walkthrough

This PR updates the Raydium LaunchLab IDL to version 0.2.0, introducing renamed instructions, expanded type definitions, and new PDA structures. It adds an IDL parser utility for decoding Solana Anchor instructions and account data, and provides four complete trading examples demonstrating buy/sell operations on Raydium LaunchLab with manual account derivation and transaction construction.

Changes

Cohort / File(s) Summary
IDL Schema Update
idl/raydium_launchlab_idl.json
Version bumped from 0.1.0 to 0.2.0. Instructions renamed: claim_platform_feeclaim_creator_fee, collect_feeclaim_platform_fee_from_vault. Added new instructions for platform curve/config management (remove_platform_curve_param, update_platform_curve_param, initialize_with_token_2022, initialize_v2). Introduced new public types: AmmCreatorFeeOn, BondingCurveParam, PlatformCurveParam, PlatformConfigInfo, PlatformParams, PlatformConfigParam (enum), TransferFeeExtensionParams, VestingParams, VestingRecord. Expanded account declarations with new PDAs and program references. Updated error codes.
IDL Parser Infrastructure
learning-examples/letsbonk-buy-sell/idl_parser.py
New module providing IDLParser class for loading and parsing Solana Anchor IDLs. Public API: get_instruction_discriminators(), get_instruction_names(), validate_instruction_data_length(), decode_instruction(), decode_account_data(). Includes type-size calculation, discriminator computation, and support for decoding structs, enums, arrays, and primitives. Public constants: DISCRIMINATOR_SIZE, PUBLIC_KEY_SIZE, STRING_LENGTH_PREFIX_SIZE, ENUM_DISCRIMINATOR_SIZE. Utility: load_idl_parser() factory function.
Buy/Sell Trading Examples
learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py, manual_buy_exact_out.py, manual_sell_exact_in.py, manual_sell_exact_out.py
Four new end-to-end trading scripts implementing buy/sell operations on Raydium LaunchLab. Each includes: PDA derivation helpers (derive_authority_pda, derive_event_authority_pda, derive_pool_state_for_token, derive_creator_fee_vault, derive_platform_fee_vault); pool state decoding (decode_pool_state, get_pool_state_data); ATA utilities (get_associated_token_address, create_associated_token_account_idempotent_instruction); token lifecycle helpers (create_initialize_account_instruction, create_close_account_instruction, create_wsol_account_with_seed); slippage/output calculation; and core trading function (buy_exact_in/buy_exact_out/sell_exact_in/sell_exact_out) performing full transaction simulation and submission with compute budget configuration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Script as Trading Script
    participant IDL as IDL Parser
    participant RPC as Solana RPC
    participant Program as Raydium Program

    User->>Script: Invoke with token/amount
    Script->>Script: Derive PDAs & vaults
    Script->>RPC: Fetch pool state
    RPC-->>Script: Raw account data
    Script->>IDL: Decode pool state
    IDL-->>Script: Decoded pool struct
    Script->>Script: Calculate min/max output with slippage
    Script->>Script: Build tx: compute budget + ATA + WSOL + instruction
    Script->>RPC: Simulate transaction
    alt Simulation Success
        Script->>RPC: Send transaction
        RPC-->>Program: Execute instruction
        Program-->>RPC: Confirm
        RPC-->>Script: Tx signature
        Script-->>User: Success + Solscan link
    else Simulation Fails
        Script-->>User: Error message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

This change spans heterogeneous areas: (1) significant IDL schema restructuring with renamed entities and new types requiring semantic verification; (2) a new utility module with multiple public APIs and dense type-handling logic; (3) four similar but distinct trading scripts with repetitive patterns yet significant logic density around PDA derivation, account sequencing, and transaction assembly. The review requires careful attention to IDL correctness, IDL parser robustness across type variants, and logic validation in each trading example's transaction construction and slippage calculations.

Poem

🐰 Hops excitedly through the changes

IDL bumped to versions new,
Parser tools for Anchor too!
Buy and sell with perfect grace,
Four examples set the pace—
Trading flows with seeds so true,
Raydium dances—what a view! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The PR title "Feat/letsbonk examples" is related to the primary changes in this pull request, which centers on adding buy/sell transaction examples for the Letsbonk/Raydium LaunchLab program. However, the title is somewhat generic and vague, using only broad terms ("Letsbonk examples") without specifying what functionality is being demonstrated or what the examples accomplish. While the title does capture the main practical addition to the repository, it lacks clarity about whether these are new examples, fixes to existing ones, or how they relate to the infrastructure changes (IDL update and parser module) that accompany them. Consider revising the PR title to be more specific and descriptive, such as "Add Letsbonk buy/sell transaction examples with IDL parser and support" or "Add manual buy/sell flow examples for Raydium LaunchLab". This would better communicate the scope of changes including the IDL update, parser module, and transaction examples, making the pull request's purpose clearer when scanning history.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed The PR objectives state the changes address issue #139 ("Not enough remaining accounts error in Letsbonk examples") by moving examples from an archived repository and including a fix. The code changes support this objective through an updated IDL with extended account structures and PDAs (raydium_launchlab_idl.json version bump 0.1.0 → 0.2.0), a new IDL parser module for account decoding, and four working transaction example scripts. Each example script properly derives required PDAs, constructs comprehensive account lists (including creator/platform fee vaults), and demonstrates correct transaction assembly. The changes logically address the "not enough remaining accounts" error by providing complete, functional examples with all necessary accounts included.
Out of Scope Changes Check ✅ Passed All code changes in this pull request are directly in scope for the stated objectives. The IDL update (raydium_launchlab_idl.json) is necessary to align with the current Raydium LaunchLab program and is fundamental to the examples working correctly. The new IDL parser module (idl_parser.py) provides essential utilities for the Python examples to decode account data based on the IDL. The four manual transaction example scripts (manual_buy_exact_in.py, manual_buy_exact_out.py, manual_sell_exact_in.py, manual_sell_exact_out.py) are precisely the "Letsbonk buy/sell examples" referenced in the PR objectives. No unrelated changes, refactorings, or improvements outside these categories are present.
Docstring Coverage ✅ Passed Docstring coverage is 98.80% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/letsbonk-examples

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@smypmsa smypmsa self-assigned this Oct 21, 2025
@smypmsa smypmsa added bug Something isn't working enhancement New feature or request labels Oct 21, 2025
@smypmsa smypmsa merged commit 25c376d into main Oct 21, 2025
1 check was pending
@smypmsa smypmsa deleted the feat/letsbonk-examples branch October 21, 2025 22:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (22)
idl/raydium_launchlab_idl.json (1)

200-373: Trade instruction account lists still omit fee-vaults; rely on remaining accounts.

buy_exact_out keeps 15 main accounts and does not list creator/platform fee vaults, which the program now expects via remaining accounts. The examples add these (plus System Program), resolving the "NotEnoughRemainingAccounts" failure. Consider documenting these three remaining accounts in the docs array to avoid confusion.

learning-examples/letsbonk-buy-sell/idl_parser.py (3)

24-41: Annotate mutable class attribute with ClassVar and refine typing.

Add ClassVar to satisfy RUF012 and strengthen types for the primitive map.
Apply this diff:

-from typing import Any
+from typing import Any, ClassVar, Tuple
@@
-    _PRIMITIVE_TYPE_INFO = {
+    _PRIMITIVE_TYPE_INFO: ClassVar[dict[str, Tuple[str | None, int]]] = {

42-59: Use centralized logger instead of print for diagnostics.

Replace print-based verbose paths with get_logger(name) to follow repo logging guidance.
Apply this diff:

+from src.utils.logger import get_logger
@@
-        self.verbose = verbose
+        self.verbose = verbose
+        self._logger = get_logger(__name__)
@@
-                print(
+                self._logger.warning(
                     f"⚠️  Instruction data for '{instruction_name}' is shorter than the expected minimum "
                     f"({actual_size}/{expected_min_size} bytes)."
                 )
@@
-                    print(f"❌ Decode error in argument '{arg['name']}': {e}")
+                    self._logger.exception("Decode error in argument '%s'", arg["name"])
@@
-                    print(f"Account type '{account_type_name}' not found in IDL")
+                    self._logger.error("Account type '%s' not found in IDL", account_type_name)
@@
-                        print(
+                        self._logger.error(
                             f"Account data too short to contain a discriminator: {len(account_data)} bytes"
                         )
@@
-                print(f"Error decoding account data for {account_type_name}: {e}")
+                self._logger.exception("Error decoding account data for %s", account_type_name)
@@
-                    print(f"📏 Initialize instruction min size: {min_size} bytes")
+                    self._logger.debug("Initialize instruction min size: %d bytes", min_size)
@@
-                    print(f"⚠️  Could not calculate size for {instruction['name']}: {e}")
+                    self._logger.warning("Could not calculate size for %s", instruction["name"], exc_info=True)

118-123: Avoid blind except Exception; either narrow or re-raise after logging.

At least catch (ValueError, KeyError, struct.error) where applicable; keep verbose path logging.

learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py (5)

549-556: Avoid hardcoded rent; fetch minimum balance for rent exemption.

Token account rent is network-dependent. Query via RPC and add to the deposit to prevent “insufficient funds” or close failures.
Example change inside buy_exact_out():

-        account_creation_lamports = 2_039_280  # Standard account creation cost
+        rent_resp = await client.get_minimum_balance_for_rent_exemption(165)
+        account_creation_lamports = rent_resp.value

375-377: Call SyncNative after initializing WSOL.

After funding and initializing the native mint account, add SyncNative (instruction 17) so token amount matches lamports.
Apply this diff:

+def create_sync_native_instruction(account: Pubkey) -> Instruction:
+    # SPL Token: SyncNative (instruction 17)
+    return Instruction(
+        program_id=TOKEN_PROGRAM_ID,
+        data=bytes([17]),
+        accounts=[AccountMeta(pubkey=account, is_signer=False, is_writable=True)],
+    )
@@
         instructions = [
             set_compute_unit_price(COMPUTE_UNIT_PRICE),
             set_compute_unit_limit(COMPUTE_UNIT_LIMIT),
             create_ata_ix,
             create_wsol_ix,
             init_wsol_ix,
+            create_sync_native_instruction(user_quote_token),
             buy_exact_out_ix,
             close_wsol_ix,
         ]

Also applies to: 640-654


713-719: Add wallet balance check versus total_lamports before sending.

Prevent runtime failures by ensuring balance covers WSOL funding + fees.


22-35: Import ordering and linting.

Per guidelines, place standard lib, then third-party, then local imports. Move from idl_parser import load_idl_parser below third-party imports; also enforce 88-char lines.


686-696: Retries/backoff on send_transaction (examples).

Optional: add simple exponential backoff retry on transient RPC errors to improve reliability.

learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py (4)

424-441: Min-out math is conservative; ok. Consider fees.

Using virtual reserves with a slippage buffer works, but does not account for protocol/platform/creator fees. If available, incorporate fee rates from Global/PlatformConfig for tighter bounds.


538-545: Use dynamic rent instead of hardcoded constant for WSOL account.

Same rationale as buy_exact_out; query get_minimum_balance_for_rent_exemption(165).


373-376: Add SyncNative after initializing WSOL.

Mirror the change suggested in buy_exact_out to keep WSOL amount in sync.
Apply the same helper and insertion before the swap instruction.

Also applies to: 629-643


22-35: Import ordering and 88-char wrapping.

Move local idl_parser import after third-party imports and wrap long lines to ≤88 chars.

learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py (5)

668-679: Add retries with exponential backoff and handle common transient RPC errors

Single send + confirm is fragile. Implement minimal retries for BlockhashNotFound, AccountInUse, NodeBehind, etc., and refresh blockhash when needed.

-        print("Sending transaction...")
-        result = await client.send_transaction(
-            transaction,
-            opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed),
-        )
+        print("Sending transaction...")
+        result = None
+        for attempt in range(3):
+            try:
+                result = await client.send_transaction(
+                    transaction,
+                    opts=TxOpts(skip_preflight=False, preflight_commitment=Confirmed),
+                )
+                break
+            except Exception as send_err:
+                # Refresh blockhash once and retry with backoff
+                if attempt == 1:
+                    bh = await client.get_latest_blockhash()
+                    recent_blockhash = bh.value.blockhash
+                    message = Message.new_with_blockhash(
+                        instructions, PAYER.pubkey(), recent_blockhash
+                    )
+                    transaction = VersionedTransaction(message, [PAYER])
+                await asyncio.sleep(0.5 * (2**attempt))
+        if result is None:
+            print("Failed to send transaction after retries")
+            return None

As per coding guidelines on retries and blockhash caching.


87-89: Tune priority fee; make it configurable and adaptive

1_000 micro‑lamports/CU is often too low. Make it env-configurable and consider bumping on simulation “insufficient priority fee”.

-COMPUTE_UNIT_LIMIT = 150_000
-COMPUTE_UNIT_PRICE = 1_000
+COMPUTE_UNIT_LIMIT = int(os.environ.get("COMPUTE_UNIT_LIMIT", "200000"))
+COMPUTE_UNIT_PRICE = int(os.environ.get("COMPUTE_UNIT_PRICE", "30000"))  # µ-lamports

682-687: Replace prints + broad except with structured logging and narrower exceptions

Use centralized logger and log exceptions with context; avoid bare except Exception. This also satisfies Ruff BLE001/TRY300.

+from src.utils.logger import get_logger
+logger = get_logger(__name__)
@@
-        print(f"Error decoding pool state: {e}")
-        import traceback
-        traceback.print_exc()
+        logger.exception("Error decoding pool state")
@@
-    except Exception as e:
-        print(f"Error getting pool state data: {e}")
+    except ValueError as e:
+        logger.error("Pool state account not found: %s", e)
+        return None
+    except Exception:
+        logger.exception("Error getting pool state data")
         return None
@@
-    except Exception as e:
-        print(f"Error executing sell_exact_in: {e}")
-        import traceback
-        traceback.print_exc()
+    except Exception:
+        logger.exception("Error executing sell_exact_in")
         return None
@@
-    except ValueError as e:
-        print(f"Invalid token mint address: {e}")
+    except ValueError as e:
+        logger.error("Invalid token mint address: %s", e)
         sys.exit(1)
-    except Exception as e:
-        print(f"Error: {e}")
-        import traceback
-        traceback.print_exc()
+    except Exception:
+        logger.exception("Unhandled error in main")
         sys.exit(1)

As per coding guidelines (logger, BLE/TRY).

Also applies to: 200-206, 227-230, 451-454, 742-751


181-199: Type hints: prefer precise dict[str, Any] over bare dict

Improve signatures for static analysis and readability.

-def decode_pool_state(account_data: bytes) -> dict | None:
+from typing import Any
+def decode_pool_state(account_data: bytes) -> dict[str, Any] | None:

Repeat similarly where dict is used in this file.


441-447: Line length > 88 chars; wrap long f-strings

Several print/log lines exceed 88 chars. Please wrap to comply with Ruff and project style.

Example:

-        print(f"Minimum with {slippage_tolerance * 100}% slippage: {minimum_with_slippage:,} lamports ({minimum_with_slippage / LAMPORTS_PER_SOL:.6f} SOL)")
+        print(
+            f"Minimum with {slippage_tolerance * 100}% slippage: "
+            f"{minimum_with_slippage:,} lamports "
+            f"({minimum_with_slippage / LAMPORTS_PER_SOL:.6f} SOL)"
+        )

As per coding guidelines.

learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py (4)

669-680: Add retry/backoff and blockhash refresh on send/confirm path

Mirror the retry approach suggested in the other file; reduce flakes and align with guidelines.

Same diff as proposed for manual_sell_exact_in.py send/confirm block.


87-89: Priority fee defaults are low; make configurable and bump

Use env-driven defaults and consider adaptive bumps when simulation suggests low priority.

-COMPUTE_UNIT_LIMIT = 150_000
-COMPUTE_UNIT_PRICE = 1_000
+COMPUTE_UNIT_LIMIT = int(os.environ.get("COMPUTE_UNIT_LIMIT", "200000"))
+COMPUTE_UNIT_PRICE = int(os.environ.get("COMPUTE_UNIT_PRICE", "30000"))

684-690: Use centralized logger and avoid broad except

Switch prints to logger and narrow exceptions; satisfies Ruff BLE001/TRY300.

+from src.utils.logger import get_logger
+logger = get_logger(__name__)
@@
-    except Exception as e:
-        print(f"Error executing sell_exact_out: {e}")
-        import traceback
-        traceback.print_exc()
+    except Exception:
+        logger.exception("Error executing sell_exact_out")
         return None
@@
-    except ValueError as e:
-        print(f"Invalid token mint address: {e}")
+    except ValueError as e:
+        logger.error("Invalid token mint address: %s", e)
         sys.exit(1)
-    except Exception as e:
-        print(f"Error: {e}")
-        import traceback
-        traceback.print_exc()
+    except Exception:
+        logger.exception("Unhandled error in main")
         sys.exit(1)

As per coding guidelines.

Also applies to: 200-206, 227-230, 454-456, 743-751


181-199: Tighten type hints to dict[str, Any]

Improve static typing on decode_pool_state and other dict-returning functions.

-from typing import Any
-def decode_pool_state(account_data: bytes) -> dict | None:
+from typing import Any
+def decode_pool_state(account_data: bytes) -> dict[str, Any] | None:

Repeat where applicable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e97e2f2 and b2ea341.

📒 Files selected for processing (6)
  • idl/raydium_launchlab_idl.json (29 hunks)
  • learning-examples/letsbonk-buy-sell/idl_parser.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)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/letsbonk-buy-sell/idl_parser.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_out.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 developers

Always test changes using scripts in learning-examples/ before modifying the main bot

Files:

  • learning-examples/letsbonk-buy-sell/idl_parser.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py
learning-examples/**

📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)

Place integration tests and validation scenarios under learning-examples/

Files:

  • learning-examples/letsbonk-buy-sell/idl_parser.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_out.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_buy_exact_in.py
  • learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py
🧬 Code graph analysis (4)
learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py (1)
learning-examples/letsbonk-buy-sell/idl_parser.py (2)
  • load_idl_parser (380-391)
  • decode_account_data (144-185)
learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py (2)
learning-examples/letsbonk-buy-sell/idl_parser.py (2)
  • load_idl_parser (380-391)
  • decode_account_data (144-185)
src/core/wallet.py (2)
  • keypair (29-31)
  • pubkey (24-26)
learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py (2)
learning-examples/letsbonk-buy-sell/idl_parser.py (2)
  • load_idl_parser (380-391)
  • decode_account_data (144-185)
src/core/client.py (4)
  • get_account_info (96-114)
  • get_latest_blockhash (131-139)
  • confirm_transaction (209-229)
  • get_token_account_balance (116-129)
learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py (3)
learning-examples/letsbonk-buy-sell/idl_parser.py (2)
  • load_idl_parser (380-391)
  • decode_account_data (144-185)
src/core/wallet.py (2)
  • keypair (29-31)
  • pubkey (24-26)
src/core/client.py (4)
  • get_account_info (96-114)
  • get_latest_blockhash (131-139)
  • confirm_transaction (209-229)
  • get_token_account_balance (116-129)
🪛 Ruff (0.14.1)
learning-examples/letsbonk-buy-sell/idl_parser.py

24-40: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


119-119: Do not catch blind exception: Exception

(BLE001)


180-180: Consider moving this statement to an else block

(TRY300)


182-182: Do not catch blind exception: Exception

(BLE001)


213-213: Do not catch blind exception: Exception

(BLE001)


232-234: Avoid specifying long messages outside the exception class

(TRY003)


253-253: Avoid specifying long messages outside the exception class

(TRY003)


276-278: Avoid specifying long messages outside the exception class

(TRY003)


294-294: Avoid specifying long messages outside the exception class

(TRY003)


312-312: Avoid specifying long messages outside the exception class

(TRY003)


335-335: Avoid specifying long messages outside the exception class

(TRY003)


352-354: Avoid specifying long messages outside the exception class

(TRY003)


377-377: Avoid specifying long messages outside the exception class

(TRY003)

learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py

196-196: Consider moving this statement to an else block

(TRY300)


198-198: Do not catch blind exception: Exception

(BLE001)


225-225: Do not catch blind exception: Exception

(BLE001)


442-442: Consider moving this statement to an else block

(TRY300)


444-444: Do not catch blind exception: Exception

(BLE001)


678-678: Consider moving this statement to an else block

(TRY300)


680-680: Do not catch blind exception: Exception

(BLE001)


725-725: Do not catch blind exception: Exception

(BLE001)

learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py

198-198: Consider moving this statement to an else block

(TRY300)


200-200: Do not catch blind exception: Exception

(BLE001)


227-227: Do not catch blind exception: Exception

(BLE001)


432-434: Abstract raise to an inner function

(TRY301)


432-434: Avoid specifying long messages outside the exception class

(TRY003)


452-452: Consider moving this statement to an else block

(TRY300)


454-454: Do not catch blind exception: Exception

(BLE001)


689-689: Consider moving this statement to an else block

(TRY300)


691-691: Do not catch blind exception: Exception

(BLE001)


730-730: Do not catch blind exception: Exception

(BLE001)

learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py

198-198: Consider moving this statement to an else block

(TRY300)


200-200: Do not catch blind exception: Exception

(BLE001)


227-227: Do not catch blind exception: Exception

(BLE001)


449-449: Consider moving this statement to an else block

(TRY300)


451-451: Do not catch blind exception: Exception

(BLE001)


680-680: Consider moving this statement to an else block

(TRY300)


682-682: Do not catch blind exception: Exception

(BLE001)


728-728: Do not catch blind exception: Exception

(BLE001)


745-745: Do not catch blind exception: Exception

(BLE001)

learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py

198-198: Consider moving this statement to an else block

(TRY300)


200-200: Do not catch blind exception: Exception

(BLE001)


227-227: Do not catch blind exception: Exception

(BLE001)


452-452: Consider moving this statement to an else block

(TRY300)


454-454: Do not catch blind exception: Exception

(BLE001)


682-682: Consider moving this statement to an else block

(TRY300)


684-684: Do not catch blind exception: Exception

(BLE001)


729-729: Do not catch blind exception: Exception

(BLE001)


746-746: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (18)
idl/raydium_launchlab_idl.json (11)

5-5: Version bump looks correct and aligns with new features.

IDL metadata version set to "0.2.0" matches added instructions/types and error codes used by the new examples.


390-559: Creator fee vault PDA seeds match example derivation.

claim_creator_fee shows creator_fee_vault seeds [creator, quote_mint] and an authority seed, which aligns with the examples’ derive_creator_fee_vault(creator, quote_mint).
Please confirm that all LaunchLab pools use the same quote mint (WSOL in Letsbonk) so the examples’ PDA derivations are universally correct.


563-726: Platform fee collection path consistent with examples.

claim_platform_fee indicates platform_fee_wallet-gated collection and uses recipient_token_account via ATA seeds; examples derive the fee vault via [platform_config, quote_mint], matching claim_platform_fee_from_vault.


913-1108: Vesting additions are coherent; no conflicts with trading examples.

Vesting accounts/events don’t impact buy/sell flows directly; schema looks self-consistent.


1206-1324: Protocol/migrate fee collection reflects new owners; good separation.

collect_fee and collect_migrate_fee clearly separate protocol and migrate fee owners; no action for examples.


1439-1511: create_platform_config includes cpswap_config and transfer-fee authority.

New accounts support post-migration parameters; consistent with later migration instructions.


1653-1930: initialize: PDAs and programs align; token program addresses pinned.

Seeds for pool_state/base_vault/quote_vault match examples’ derivations.


1932-2235: initialize_v2 adds AmmCreatorFeeOn; ensure caller tooling supports it.

Examples don’t initialize pools, but downstream tools should be aware of the new enum.


2237-2527: Token-2022 init path is explicit and uses Token-2022 program id.

Clear separation reduces ambiguity in examples (which use classic Token program).


3554-3623: Platform curve param remove/update flows added; good admin surface.

No direct impact to examples.


4334-4438: New error 6018 NotEnoughRemainingAccounts directly addresses #139.

This clarifies the failure observed in examples and justifies adding remaining accounts in scripts.

learning-examples/letsbonk-buy-sell/idl_parser.py (1)

250-279: Enum size computation looks correct; keep comment.

Largest-variant sizing plus 1-byte discriminator aligns with Anchor layout.

learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py (2)

609-620: Fix for “NotEnoughRemainingAccounts” is correct.

Adding System Program, platform_fee_vault, and creator_fee_vault as remaining accounts resolves the program’s new requirement (IDL error 6018).
Run a dry-run against a known pool to confirm no “Not enough remaining accounts” error persists.


141-179: PDA seeds align with IDL; good.

creator_fee_vault seeds [creator, quote_mint] and platform_fee_vault seeds [platform_config, quote_mint] match the IDL.

learning-examples/letsbonk-buy-sell/manual_buy_exact_in.py (2)

598-609: Fix for “NotEnoughRemainingAccounts” is correct here too.

System Program + platform_fee_vault + creator_fee_vault appended; aligns with IDL and resolves #139.


690-721: Nice: balance check present.

This prevents needless simulation/sends. Keep it.

learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py (1)

602-613: Good: remaining accounts appended — likely fixes “Not enough remaining accounts”

Adding System Program, platform fee vault, and creator fee vault as remaining accounts aligns with how LaunchLab collects fees. This should resolve issue #139. Please confirm via simulation on a small trade.

Would you run the script with a tiny amount and share the simulation/confirmation logs to confirm the error is gone?

learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py (1)

604-615: Good: remaining accounts added — addresses the linked error

Appending System Program + platform/creator fee vaults should satisfy the program’s remaining accounts requirement. Likely resolves “Not enough remaining accounts”.

Please confirm by running a small sell_exact_out and sharing the simulation success.

Comment on lines +431 to +443
if virtual_base <= amount_out:
raise ValueError(
f"Amount out ({amount_out}) cannot be >= virtual base reserves ({virtual_base})"
)

# Calculate required input using rearranged constant product formula
numerator = amount_out * virtual_quote
denominator = virtual_base - amount_out
expected_input = numerator // denominator

# Apply slippage tolerance (add buffer for price movement)
maximum_with_slippage = int(expected_input * (1 + slippage_tolerance))

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 | 🟠 Major

Use ceil for required input to avoid underfunding.

Floor division can underestimate amount_in; use integer ceil to ensure enough input before slippage.
Apply this diff:

-        expected_input = numerator // denominator
+        # ceil(numerator / denominator)
+        expected_input = (numerator + denominator - 1) // denominator
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if virtual_base <= amount_out:
raise ValueError(
f"Amount out ({amount_out}) cannot be >= virtual base reserves ({virtual_base})"
)
# Calculate required input using rearranged constant product formula
numerator = amount_out * virtual_quote
denominator = virtual_base - amount_out
expected_input = numerator // denominator
# Apply slippage tolerance (add buffer for price movement)
maximum_with_slippage = int(expected_input * (1 + slippage_tolerance))
if virtual_base <= amount_out:
raise ValueError(
f"Amount out ({amount_out}) cannot be >= virtual base reserves ({virtual_base})"
)
# Calculate required input using rearranged constant product formula
numerator = amount_out * virtual_quote
denominator = virtual_base - amount_out
# ceil(numerator / denominator)
expected_input = (numerator + denominator - 1) // denominator
# Apply slippage tolerance (add buffer for price movement)
maximum_with_slippage = int(expected_input * (1 + slippage_tolerance))
🧰 Tools
🪛 Ruff (0.14.1)

432-434: Abstract raise to an inner function

(TRY301)


432-434: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_buy_exact_out.py around lines 431
to 443, the code uses floor division for expected_input which can under-estimate
required amount_in; replace the integer floor division with an integer ceiling
division (e.g. expected_input = (numerator + denominator - 1) // denominator or
use math.ceil(numerator / denominator)), and compute maximum_with_slippage using
a ceiling as well (e.g. maximum_with_slippage = math.ceil(expected_input * (1 +
slippage_tolerance))); add the needed import for math if using math.ceil.

Comment on lines +36 to +39
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Broken module import path for idl_parser; make path deterministic

Import will fail because sys.path is bumped two levels up while idl_parser.py lives alongside this script. Also hardcoded relative IDL path is brittle. Fix by appending this file’s directory to sys.path and resolving the IDL path from file.

Apply:

+import pathlib
@@
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
-IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
+BASE_DIR = pathlib.Path(__file__).resolve().parent
+if str(BASE_DIR) not in sys.path:
+    sys.path.append(str(BASE_DIR))
+# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
+IDL_PATH = BASE_DIR / "idl" / "raydium_launchlab_idl.json"
+IDL_PARSER = load_idl_parser(str(IDL_PATH), verbose=True)

And keep from idl_parser import load_idl_parser as-is so it resolves from BASE_DIR. As per coding guidelines.

Also applies to: 24-25

🤖 Prompt for AI Agents
learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py around lines 36 to
39 (also applies to lines 24-25): the current sys.path modification jumps two
directories up and the IDL path is a hardcoded relative path which breaks module
resolution; instead append this file's directory (os.path.dirname(__file__)) to
sys.path so idl_parser.py resolves reliably, and build the IDL JSON path using
os.path.join(os.path.dirname(__file__), "idl", "raydium_launchlab_idl.json")
(keep the existing from idl_parser import load_idl_parser import as-is).

Comment on lines +47 to +50
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
PAYER = Keypair.from_bytes(PRIVATE_KEY)

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 | 🟠 Major

Validate env vars before use to avoid crashes and improve UX

Decoding a missing SOLANA_PRIVATE_KEY or using a None RPC endpoint will throw. Validate early with clear errors.

-RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
-PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
-PAYER = Keypair.from_bytes(PRIVATE_KEY)
+RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
+PRIVATE_KEY_B58 = os.environ.get("SOLANA_PRIVATE_KEY")
+if not RPC_ENDPOINT or not PRIVATE_KEY_B58:
+    raise EnvironmentError(
+        "Set SOLANA_NODE_RPC_ENDPOINT and SOLANA_PRIVATE_KEY environment variables."
+    )
+PRIVATE_KEY = base58.b58decode(PRIVATE_KEY_B58)
+PAYER = Keypair.from_bytes(PRIVATE_KEY)

Based on coding guidelines.

🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py around lines 47
to 50, the code reads SOLANA_NODE_RPC_ENDPOINT and SOLANA_PRIVATE_KEY from the
environment and immediately decodes/uses them which will crash if either is
missing or malformed; add explicit validation: verify SOLANA_NODE_RPC_ENDPOINT
is present and non-empty and raise/exit with a clear error if not, read
SOLANA_PRIVATE_KEY and check it is present before attempting base58 decode, wrap
the base58.b58decode call in try/except to catch decoding errors and raise a
clear message, and only call Keypair.from_bytes after successful decode; ensure
error messages state which env var is missing or invalid so the user can fix it.

Comment on lines +219 to +226
try:
account_info = await client.get_account_info(pool_state)
if not account_info.value:
print("Pool state account not found")
return None

return decode_pool_state(account_info.value.data)

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

Decode account data bytes correctly; request base64 and decode

decode_pool_state expects bytes, but get_account_info returns base64-encoded data. Ask RPC for base64 and decode before passing to decoder.

-        account_info = await client.get_account_info(pool_state)
+        import base64
+        account_info = await client.get_account_info(pool_state, encoding="base64")
@@
-        return decode_pool_state(account_info.value.data)
+        data_b64, _encoding = account_info.value.data
+        return decode_pool_state(base64.b64decode(data_b64))

As per relevant snippets in src/core/client.py.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py around lines
219-226, the code passes account_info.value.data (which is base64 text) directly
to decode_pool_state which expects raw bytes; update the RPC call to request
base64-encoded data (e.g., pass encoding="base64" or the equivalent parameter
used by our client), then base64-decode account_info.value.data into bytes and
pass those bytes into decode_pool_state, preserving the existing presence check
and return path.

Comment on lines +543 to +548
# Calculate required lamports (minimal amount for account creation)
account_creation_lamports = 2_039_280 # Standard account creation cost

user_quote_token, create_wsol_ix, init_wsol_ix = create_wsol_account_with_seed(
PAYER.pubkey(), wsol_seed, account_creation_lamports
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid hardcoding rent-exempt lamports; query RPC

Rent exemption changes over time. Use RPC to fetch the exact minimum for 165-byte token accounts.

-        # Calculate required lamports (minimal amount for account creation)
-        account_creation_lamports = 2_039_280  # Standard account creation cost
+        # Calculate required lamports (rent-exempt) for a 165-byte token account
+        account_creation_lamports = await client.get_minimum_balance_for_rent_exemption(
+            165
+        )
+        if hasattr(account_creation_lamports, "value"):
+            account_creation_lamports = account_creation_lamports.value
🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_in.py around lines 543
to 548, the lamports for rent-exemption are hardcoded; replace that with an RPC
query to fetch the precise minimum for a 165-byte token account using your
Solana connection (call get_minimum_balance_for_rent_exemption(165) or the async
equivalent), assign the returned value to account_creation_lamports, and use
that variable when creating the WSOL account; if the connection call can fail,
add a short fallback or raise a clear error so the code does not silently
continue with an incorrect hardcoded value.

Comment on lines +36 to +39
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Same import-path fragility for idl_parser and IDL file path

Mirror the import/path fix from the other script: append this dir to sys.path and resolve IDL JSON from file.

+import pathlib
@@
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
-IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
+BASE_DIR = pathlib.Path(__file__).resolve().parent
+if str(BASE_DIR) not in sys.path:
+    sys.path.append(str(BASE_DIR))
+# Initialize IDL parser for Raydium LaunchLab with verbose mode for debugging
+IDL_PATH = BASE_DIR / "idl" / "raydium_launchlab_idl.json"
+IDL_PARSER = load_idl_parser(str(IDL_PATH), verbose=True)

Also applies to: 24-25

🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py around lines
36-39 (also applies to lines 24-25), the current sys.path manipulation and IDL
JSON reference are fragile; change it to append the script's directory
(os.path.dirname(__file__)) to sys.path and resolve the IDL file path relative
to __file__ (e.g. build the absolute path with
os.path.join(os.path.dirname(__file__), 'idl', 'raydium_launchlab_idl.json'))
before calling load_idl_parser so the import and JSON file load work reliably
regardless of CWD.

Comment on lines +47 to +50
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
PAYER = Keypair.from_bytes(PRIVATE_KEY)

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 | 🟠 Major

Guard env reads (RPC endpoint, private key) with explicit validation

Avoid TypeError on base58 decode and give clear guidance when env vars are missing.

-RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
-PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
-PAYER = Keypair.from_bytes(PRIVATE_KEY)
+RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
+PRIVATE_KEY_B58 = os.environ.get("SOLANA_PRIVATE_KEY")
+if not RPC_ENDPOINT or not PRIVATE_KEY_B58:
+    raise EnvironmentError(
+        "Set SOLANA_NODE_RPC_ENDPOINT and SOLANA_PRIVATE_KEY environment variables."
+    )
+PRIVATE_KEY = base58.b58decode(PRIVATE_KEY_B58)
+PAYER = Keypair.from_bytes(PRIVATE_KEY)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
PAYER = Keypair.from_bytes(PRIVATE_KEY)
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
PRIVATE_KEY_B58 = os.environ.get("SOLANA_PRIVATE_KEY")
if not RPC_ENDPOINT or not PRIVATE_KEY_B58:
raise EnvironmentError(
"Set SOLANA_NODE_RPC_ENDPOINT and SOLANA_PRIVATE_KEY environment variables."
)
PRIVATE_KEY = base58.b58decode(PRIVATE_KEY_B58)
PAYER = Keypair.from_bytes(PRIVATE_KEY)

Comment on lines +219 to +226
try:
account_info = await client.get_account_info(pool_state)
if not account_info.value:
print("Pool state account not found")
return None

return decode_pool_state(account_info.value.data)

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

Decode account_info data to bytes before IDL parsing

Same as the exact-in script: request base64 encoding and decode.

-        account_info = await client.get_account_info(pool_state)
+        import base64
+        account_info = await client.get_account_info(pool_state, encoding="base64")
@@
-        return decode_pool_state(account_info.value.data)
+        data_b64, _encoding = account_info.value.data
+        return decode_pool_state(base64.b64decode(data_b64))

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py around lines
219-226, account_info.value.data is being passed directly to decode_pool_state
but must be decoded from base64 bytes first; update the get_account_info call to
request base64 encoding (e.g., encoding='base64') if not already, then when
account_info.value exists decode account_info.value.data from base64 into bytes
and pass those bytes into decode_pool_state (keep the existing missing-account
check and return logic).

Comment on lines +545 to +551
# Calculate required lamports (minimal amount for account creation)
account_creation_lamports = 2_039_280 # Standard account creation cost

user_quote_token, create_wsol_ix, init_wsol_ix = create_wsol_account_with_seed(
PAYER.pubkey(), wsol_seed, account_creation_lamports
)

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Compute rent-exempt lamports dynamically

Use RPC query instead of a magic number for token account size 165.

-        account_creation_lamports = 2_039_280  # Standard account creation cost
+        account_creation_lamports = await client.get_minimum_balance_for_rent_exemption(
+            165
+        )
+        if hasattr(account_creation_lamports, "value"):
+            account_creation_lamports = account_creation_lamports.value
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Calculate required lamports (minimal amount for account creation)
account_creation_lamports = 2_039_280 # Standard account creation cost
user_quote_token, create_wsol_ix, init_wsol_ix = create_wsol_account_with_seed(
PAYER.pubkey(), wsol_seed, account_creation_lamports
)
# Calculate required lamports (minimal amount for account creation)
account_creation_lamports = await client.get_minimum_balance_for_rent_exemption(
165
)
if hasattr(account_creation_lamports, "value"):
account_creation_lamports = account_creation_lamports.value
user_quote_token, create_wsol_ix, init_wsol_ix = create_wsol_account_with_seed(
PAYER.pubkey(), wsol_seed, account_creation_lamports
)
🤖 Prompt for AI Agents
In learning-examples/letsbonk-buy-sell/manual_sell_exact_out.py around lines 545
to 551, replace the hard-coded account_creation_lamports = 2_039_280 with a
dynamic RPC call to fetch the rent-exempt balance for a token account of size
165 (e.g. await connection.get_minimum_balance_for_rent_exemption(165) or
equivalent on the RPC client used), assign that returned value to
account_creation_lamports, and use that variable in
create_wsol_account_with_seed so the lamports are computed at runtime rather
than using a magic number.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Not enough remaining accounts error in Letsbonk examples

2 participants