Skip to content

Conversation

@smypmsa
Copy link
Member

@smypmsa smypmsa commented Nov 22, 2025

Summary by CodeRabbit

  • New Features

    • Added transaction token balance retrieval and buy transaction detail tracking.
  • Bug Fixes

    • Increased compute unit data limits across bots to resolve transaction failures.
  • Chores

    • Optimized bot configurations: adjusted hold times, buy slippage, and wait intervals.
    • Disabled one bot for optimization.
    • Enhanced trading execution with precomputed values to reduce latency.

✏️ Tip: You can customize this high-level summary in your review settings.

@smypmsa smypmsa self-assigned this Nov 22, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 22, 2025

Walkthrough

The PR updates four bot configuration files with trading parameter tuning and compute unit memory adjustments, adds transaction-aware methods to the client for balance and transaction detail retrieval, and refactors the buyer and seller classes to accept precomputed token amounts and prices, eliminating redundant RPC calls during trade execution.

Changes

Cohort / File(s) Summary
Bot Configuration Updates
bots/bot-sniper-1-geyser.yaml, bots/bot-sniper-2-logs.yaml, bots/bot-sniper-3-blocks.yaml, bots/bot-sniper-4-pp.yaml
Parameter adjustments across four sniper bots: enabled flag changes, max_hold_time reductions (for TP/SL timing), and account_data_size increases from 512KB to 12.5MB to resolve CU transaction failures. Added documentation notes referencing Nov 23, 2025 optimization.
Transaction Client Methods
src/core/client.py
Added three new methods: _get_transaction_result() helper (private) for RPC transaction fetching, get_transaction_token_balance() to retrieve post-transaction token balances, and get_buy_transaction_details() to compute tokens received and SOL spent via pre/post balance diffs.
Trading Execution Refactoring
src/trading/platform_aware.py
Refactored PlatformAwareBuyer.execute() and PlatformAwareSeller.execute() to accept precomputed token_amount and token_price parameters. Buy path now fetches actual transaction details to verify amounts; sell path uses provided values to avoid RPC delays. Added _get_sol_destination() helper with platform-specific logic for PUMP_FUN and LETS_BONK.
Exit Handler Integration
src/trading/universal_trader.py
Updated _handle_time_based_exit() to accept buy_result and pass token_amount/token_price to seller; position-based exit now uses explicit position quantities and entry prices instead of defaults. Minor formatting changes to token_program_ids.

Sequence Diagram(s)

sequenceDiagram
    participant Trader as Universal Trader
    participant Buyer as PlatformAwareBuyer
    participant Client as Client
    participant RPC as RPC Node
    participant Seller as PlatformAwareSeller
    
    Trader->>Buyer: execute(token_info)
    activate Buyer
    Note over Buyer: Build & send buy tx
    Buyer->>RPC: Send transaction
    RPC-->>Buyer: signature
    
    Buyer->>Client: get_buy_transaction_details(signature, mint, sol_dest)
    activate Client
    Client->>RPC: Fetch transaction result
    RPC-->>Client: parsed tx data
    Note over Client: Compute tokens & SOL from<br/>pre/post balances
    Client-->>Buyer: (tokens_received, sol_spent)
    deactivate Client
    
    Note over Buyer: Verify actual vs expected,<br/>override amounts if needed
    Buyer-->>Trader: TradeResult(amount, price)
    deactivate Buyer
    
    Trader->>Seller: execute(token_info, token_amount, token_price)
    activate Seller
    Note over Seller: Use precomputed values<br/>Skip RPC balance/price fetch
    Note over Seller: Build sell instructions<br/>with provided amounts
    Seller->>RPC: Send sell transaction
    RPC-->>Seller: signature
    Seller-->>Trader: TradeResult
    deactivate Seller
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring extra attention:
    • src/core/client.py: Balance diff logic in get_buy_transaction_details() — verify pre/post token balance comparison across account indices is correct for multi-token scenarios
    • src/trading/platform_aware.py: _get_sol_destination() platform-specific derivation logic for PUMP_FUN and LETS_BONK — ensure address computation matches expected bonding_curve/quote_vault behavior
    • src/trading/platform_aware.py: Buy path transaction detail fetching and override logic — confirm error handling and logging when actual values diverge from expected
    • Cross-module integration: Verify all callers of execute() in both buyer and seller are updated to pass the new token_amount and token_price parameters

Possibly related PRs

  • Fix/update letsbonk fun instructions in the bot #142: Extends TokenInfo and address provider for LETS_BONK platform config (global_config, platform_fee_vault), which complements the new _get_sol_destination() platform-specific logic added in this PR for LETS_BONK quote_vault handling.

Poem

🐰 With precomputed hops and RPC calls at bay,
We fetch the token truth without delay,
No balance guessing, just verified gains,
The sniper bots now run through faster lanes! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Reduce RPC delays when selling' directly aligns with the main changes: the PR optimizes buy and sell paths to accept precomputed token amounts and prices, eliminating RPC calls during selling and reducing transaction latency.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 fix/buy-sell-15-sec

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 added bug Something isn't working enhancement New feature or request labels Nov 22, 2025
@smypmsa smypmsa linked an issue Nov 22, 2025 that may be closed by this pull request
@sergxe
Copy link

sergxe commented Nov 23, 2025

Thanks again, planning on testing it soon. Do you say it should work fine with Chainstack growth RPC, using pumpportal?

@smypmsa smypmsa marked this pull request as ready for review November 23, 2025 10:58
@smypmsa smypmsa merged commit 9d93d85 into main Nov 23, 2025
1 check was pending
@smypmsa smypmsa deleted the fix/buy-sell-15-sec branch November 23, 2025 11:02
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: 3

🧹 Nitpick comments (4)
bots/bot-sniper-3-blocks.yaml (1)

64-66: Account data size increase is consistent with CU optimization strategy

Bumping account_data_size to 12_500_000 and documenting the 512KB failure scenario makes sense and matches the other sniper configs. Just keep this value synchronized across all bots to avoid hard-to-track behavior differences.

If you keep tuning this further, consider centralizing the “recommended” size in shared documentation so all YAMLs stay in lockstep.

bots/bot-sniper-1-geyser.yaml (1)

64-66: CU account_data_size increase matches other bots and is well-documented

The 12_500_000 value and Nov 23 note mirror the other sniper configs and clearly record the observed failure with 512KB. This consistency across bots should make future tuning and debugging easier.

If you see different behavior per RPC provider, you might want a short note in docs describing provider-specific caveats rather than repeating long comments in each YAML.

src/core/client.py (1)

390-419: Centralized getTransaction wrapper is fine; consider reusing HTTP session

_get_transaction_result encapsulates the raw getTransaction RPC with jsonParsed encoding and “confirmed” commitment, and its early returns on missing result/meta keep downstream callers simple.

One performance nit: post_rpc still creates a fresh aiohttp.ClientSession per call. Long‑running bots that call _get_transaction_result frequently would benefit from a shared session (or using AsyncClient.get_transaction) to reuse connections and reduce overhead.

As per coding guidelines about HTTP connection pooling.

src/trading/platform_aware.py (1)

270-297: Seller now relies on pre-known amount/price—API and math look consistent

The updated PlatformAwareSeller.execute:

  • Validates token_amount and token_price upfront, with clear error messages.
  • Uses those to compute token_balance and expected_sol_output, and then derives min_sol_output with slippage protection in lamports.
  • Logs the key quantities and builds the sell instruction with token_balance and min_sol_output.

This matches how UniversalTrader and the buyer now pass amount/price, and it removes duplicated RPC calls for balance and price. The only caveat is the Ruff TRY003/TRY301 hints: the longer ValueError messages may need to be shortened or moved into helper functions/constants to satisfy your exception-handling lint rules.

To keep Ruff happy, you can extract the detailed error messages into small helper functions or constants and raise shorter ValueErrors here, or centralize validation of token_amount/token_price in a shared helper.

Also applies to: 307-314, 323-336

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03a4e7b and bea3f3e.

📒 Files selected for processing (7)
  • bots/bot-sniper-1-geyser.yaml (4 hunks)
  • bots/bot-sniper-2-logs.yaml (2 hunks)
  • bots/bot-sniper-3-blocks.yaml (3 hunks)
  • bots/bot-sniper-4-pp.yaml (2 hunks)
  • src/core/client.py (1 hunks)
  • src/trading/platform_aware.py (4 hunks)
  • src/trading/universal_trader.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
bots/**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

bots/**/*.{yaml,yml}: Keep bot configurations in YAML files under bots/
Detect target platform from bot configuration files
Do not include sensitive data in bot configuration files

bots/**/*.{yaml,yml}: Edit bot instance configuration in YAML files under bots/
Start with conservative YAML settings (low buy_amount, high min_sol_balance, strict filters)

Files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-4-pp.yaml
  • bots/bot-sniper-2-logs.yaml
  • bots/bot-sniper-3-blocks.yaml
bots/**/*.{yml,yaml}

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

bots/**/*.{yml,yaml}: Store bot configurations as YAML files in bots/
Support ${VARIABLE} environment interpolation in bot YAML configs
Validate bot configurations before starting bots

Files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-4-pp.yaml
  • bots/bot-sniper-2-logs.yaml
  • bots/bot-sniper-3-blocks.yaml
**/*.{yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/trading-bot.mdc)

**/*.{yaml,yml}: Maintain a consistent YAML structure for all bot configuration files, including keys: name, platform, enabled, separate_process, env_file, rpc_endpoint, wss_endpoint, private_key, geyser.{endpoint,api_token,auth_type}, and trade.{buy_amount,buy_slippage,sell_slippage,exit_strategy,extreme_fast_mode}
Use ${VARIABLE_NAME} syntax for environment variable interpolation in YAML configs
Never hardcode sensitive values (e.g., private keys, API tokens) in YAML configuration files
Validate YAML syntax and required configuration fields
Test environment variable interpolation in configuration files

Validate YAML configuration syntax and required fields before running

Files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-4-pp.yaml
  • bots/bot-sniper-2-logs.yaml
  • bots/bot-sniper-3-blocks.yaml
**/*.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:

  • src/trading/universal_trader.py
  • src/core/client.py
  • src/trading/platform_aware.py
src/{client,trading,monitoring,platforms}/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Separate concerns into client, trading, monitoring, and platforms packages under src/

Files:

  • src/trading/universal_trader.py
  • src/trading/platform_aware.py
src/**/*.py

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

src/**/*.py: Use snake_case for all Python file names
Avoid circular imports between packages
Use asyncio.create_task() for concurrent operations
Implement proper cleanup on shutdown for async tasks/resources

Files:

  • src/trading/universal_trader.py
  • src/core/client.py
  • src/trading/platform_aware.py
src/trading/universal_*.py

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

Use the 'Universal' prefix for platform-agnostic trading components (e.g., UniversalTrader)

Files:

  • src/trading/universal_trader.py
src/trading/**/*.py

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

trading/ may depend on core/, platforms/, and interfaces/

Files:

  • src/trading/universal_trader.py
  • src/trading/platform_aware.py
src/trading/universal_trader.py

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

UniversalTrader should delegate to platform-specific traders via a factory method

Files:

  • src/trading/universal_trader.py
src/core/**/*.py

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

src/core/**/*.py: Core modules must not import from trading or monitoring
core/ may depend only on utils/ and interfaces/
Limit concurrent operations based on RPC provider limits

Files:

  • src/core/client.py
src/core/client.py

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

src/core/client.py: Use connection pooling for HTTP clients and backoff for failed requests
Cache recent blockhash and relevant account information where appropriate

Files:

  • src/core/client.py
🧠 Learnings (11)
📚 Learning: 2025-10-04T12:42:50.785Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-04T12:42:50.785Z
Learning: Applies to bots/**/*.{yaml,yml} : Start with conservative YAML settings (low buy_amount, high min_sol_balance, strict filters)

Applied to files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-4-pp.yaml
  • bots/bot-sniper-2-logs.yaml
  • bots/bot-sniper-3-blocks.yaml
📚 Learning: 2025-08-21T12:08:15.475Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/trading-bot.mdc:0-0
Timestamp: 2025-08-21T12:08:15.475Z
Learning: Applies to **/*.{yaml,yml} : Maintain a consistent YAML structure for all bot configuration files, including keys: name, platform, enabled, separate_process, env_file, rpc_endpoint, wss_endpoint, private_key, geyser.{endpoint,api_token,auth_type}, and trade.{buy_amount,buy_slippage,sell_slippage,exit_strategy,extreme_fast_mode}

Applied to files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-2-logs.yaml
📚 Learning: 2025-10-04T12:42:50.785Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-04T12:42:50.785Z
Learning: Run the main bot with `pump_bot` only after safe testing

Applied to files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-3-blocks.yaml
📚 Learning: 2025-08-21T12:08:15.475Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/trading-bot.mdc:0-0
Timestamp: 2025-08-21T12:08:15.475Z
Learning: Applies to **/*.py : Provide a YOLO Mode with continuous trading and no cooldowns

Applied to files:

  • bots/bot-sniper-1-geyser.yaml
  • bots/bot-sniper-4-pp.yaml
  • bots/bot-sniper-2-logs.yaml
  • bots/bot-sniper-3-blocks.yaml
📚 Learning: 2025-08-21T12:06:38.903Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-08-21T12:06:38.903Z
Learning: Applies to src/trading/universal_*.py : Use the 'Universal' prefix for platform-agnostic trading components (e.g., UniversalTrader)

Applied to files:

  • src/trading/universal_trader.py
📚 Learning: 2025-08-21T12:06:38.903Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-08-21T12:06:38.903Z
Learning: Applies to src/trading/universal_trader.py : UniversalTrader should delegate to platform-specific traders via a factory method

Applied to files:

  • src/trading/universal_trader.py
📚 Learning: 2025-08-21T12:08:15.475Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/trading-bot.mdc:0-0
Timestamp: 2025-08-21T12:08:15.475Z
Learning: Applies to **/*.py : Support multiple exit strategies: time_based, tp_sl, and manual

Applied to files:

  • src/trading/universal_trader.py
📚 Learning: 2025-08-21T12:08:15.475Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/trading-bot.mdc:0-0
Timestamp: 2025-08-21T12:08:15.475Z
Learning: Applies to **/*.py : Implement wallet balance checks before trading

Applied to files:

  • src/core/client.py
  • src/trading/platform_aware.py
📚 Learning: 2025-10-04T12:42:50.785Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 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:

  • bots/bot-sniper-2-logs.yaml
  • src/trading/platform_aware.py
📚 Learning: 2025-08-21T12:08:15.475Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/trading-bot.mdc:0-0
Timestamp: 2025-08-21T12:08:15.475Z
Learning: Applies to **/*.py : Apply slippage protection on all trades

Applied to files:

  • bots/bot-sniper-2-logs.yaml
📚 Learning: 2025-08-21T12:06:38.903Z
Learnt from: CR
Repo: chainstacklabs/pumpfun-bonkfun-bot PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-08-21T12:06:38.903Z
Learning: Applies to src/trading/**/*.py : trading/ may depend on core/, platforms/, and interfaces/

Applied to files:

  • src/trading/platform_aware.py
🧬 Code graph analysis (2)
src/trading/universal_trader.py (2)
src/trading/base.py (2)
  • TradeResult (20-43)
  • execute (50-59)
src/trading/platform_aware.py (2)
  • execute (46-180)
  • execute (270-391)
src/trading/platform_aware.py (5)
src/core/client.py (1)
  • get_buy_transaction_details (306-388)
src/interfaces/core.py (8)
  • TokenInfo (25-53)
  • AddressProvider (56-118)
  • platform (61-63)
  • platform (126-128)
  • platform (238-240)
  • platform (314-316)
  • Platform (17-21)
  • derive_pool_address (81-93)
src/platforms/letsbonk/address_provider.py (3)
  • platform (49-51)
  • derive_pool_address (78-98)
  • derive_quote_vault (125-148)
src/platforms/pumpfun/address_provider.py (2)
  • platform (97-99)
  • derive_pool_address (129-146)
src/trading/base.py (2)
  • execute (50-59)
  • TradeResult (20-43)
🪛 Ruff (0.14.5)
src/trading/platform_aware.py

157-160: Abstract raise to an inner function

(TRY301)


157-160: Avoid specifying long messages outside the exception class

(TRY003)


289-292: Avoid specifying long messages outside the exception class

(TRY003)


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

(TRY003)

🔇 Additional comments (11)
bots/bot-sniper-3-blocks.yaml (1)

11-11: Conservative default (bot disabled) looks good

Keeping enabled: false by default is aligned with the repo guidance to start conservatively and avoid accidental live trading; users can explicitly flip this on once tested.

Based on learnings, this matches the “start with conservative YAML settings” preference.

bots/bot-sniper-1-geyser.yaml (1)

34-35: Time-based hold and wait timings are internally consistent

With exit_strategy: "time_based", the effective hold duration is governed by wait_after_buy, and you’ve aligned that to 5 seconds along with max_hold_time: 5 (used by TP/SL mode). The comments correctly point to wait_after_buy for time-based exits, so the configuration is coherent.

Also applies to: 81-81

bots/bot-sniper-2-logs.yaml (1)

27-27: Trading and CU tuning aligned with other sniper configs

Increasing buy_slippage to 0.3, tightening max_hold_time to 15s, and bumping account_data_size to 12_500_000 brings this logs-based sniper in line with the rest of the suite. The TP/SL and time-based comments are consistent with the runtime behavior (TP/SL uses max_hold_time, time-based uses wait_after_buy).

Also applies to: 34-35, 64-66

bots/bot-sniper-4-pp.yaml (2)

32-32: Shorter max_hold_time brings PumpPortal bot in line with others

Reducing max_hold_time to 15s matches the other sniper configs and keeps TP/SL exits from lingering too long. For purely time-based exits, wait_after_buy remains the main control, so this is a safe tightening.


62-64: CU data-size change is RPC‑agnostic and should be fine with Growth + PumpPortal

The account_data_size: 12_500_000 setting and accompanying note are consistent with the other bots. This limit is enforced by the on-chain setLoadedAccountsDataSizeLimit instruction and doesn’t depend on a particular RPC provider, so it should work with a Chainstack Growth RPC endpoint as long as the node supports that compute budget feature. Still worth a quick end-to-end test with Growth + PumpPortal to confirm behavior.

src/core/client.py (2)

274-305: Token balance helper correctly uses postTokenBalances

get_transaction_token_balance cleanly filters postTokenBalances by owner and mint and returns the raw amount as an int, with safe handling for missing fields and absent meta. This is a reasonable, low-level primitive for callers that already know the mint and owner.


306-389: Buy details parsing logic is sound; handles Token2022 and bad meta defensively

The get_buy_transaction_details implementation correctly:

  • Diffs preTokenBalances/postTokenBalances by accountIndex to derive a positive token delta for the target mint (works even when the owner is not the wallet, e.g., Token2022).
  • Looks up the SOL destination by matching accountKeys against sol_destination and uses postBalances[i] - preBalances[i] to get lamports sent, with sane logging when the delta is non‑positive.

Returning (None, None) on missing or malformed meta is a good defensive default; callers already treat this as an error condition.

src/trading/universal_trader.py (3)

196-198: Formatting change for traded_token_programs is fine

Splitting traded_token_programs over multiple lines improves readability and keeps you within the 88‑character limit without changing behavior.


334-335: Cleanup token_program_ids derivation remains correct after formatting

The construction of token_program_ids from self.traded_token_programs is unchanged semantically and properly aligned with the new dict layout; cleanup behavior is preserved.


469-470: Time-based exit now correctly reuses buy_result for sell—matches new seller API

Passing buy_result into _handle_time_based_exit and then into self.seller.execute as token_amount and token_price is exactly what the new PlatformAwareSeller.execute signature expects. This avoids extra RPC reads for balance/price during simple time-based exits.

You may want a small guard to log if buy_result.amount or buy_result.price is unexpectedly None, but the current flow from PlatformAwareBuyer should always populate them on success.

Also applies to: 516-533

src/trading/platform_aware.py (1)

197-231: SOL destination helper correctly encodes pump.fun vs letsbonk semantics

_get_sol_destination’s platform-specific behavior looks right:

  • For Platform.PUMP_FUN, you use bonding_curve when present and otherwise derive it via derive_pool_address.
  • For Platform.LETS_BONK, you prefer the pre-fetched quote_vault and fall back to derive_quote_vault(token_info.mint), which matches the LetsBonk address provider’s API.

The explicit NotImplementedError for new platforms is good defensive design; it forces you to decide where SOL actually lands before enabling trading.

Comment on lines +134 to +160
# Fetch actual tokens and SOL spent from transaction
# Uses preBalances/postBalances to get exact amounts
sol_destination = self._get_sol_destination(
token_info, address_provider
)
tokens_raw, sol_spent = await self.client.get_buy_transaction_details(
str(tx_signature), token_info.mint, sol_destination
)

if tokens_raw is not None and sol_spent is not None:
actual_amount = tokens_raw / 10**TOKEN_DECIMALS
actual_price = (sol_spent / LAMPORTS_PER_SOL) / actual_amount
logger.info(
f"Actual tokens received: {actual_amount:.6f} "
f"(expected: {token_amount:.6f})"
)
logger.info(
f"Actual SOL spent: {sol_spent / LAMPORTS_PER_SOL:.10f} SOL"
)
logger.info(f"Actual price: {actual_price:.10f} SOL/token")
token_amount = actual_amount
token_price_sol = actual_price
else:
raise ValueError(
f"Failed to parse transaction details: tokens={tokens_raw}, "
f"sol_spent={sol_spent}"
)
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

Parsing actual buy amounts via tx meta is correct but currently hard-fails on RPC issues

The new block that calls _get_sol_destination and get_buy_transaction_details is a solid improvement: it derives exact tokens_raw and sol_spent from pre/postTokenBalances and pre/postBalances, logs actual vs expected, and propagates the adjusted token_amount/token_price_sol into the TradeResult.

Right now, if tokens_raw or sol_spent is None (e.g., transient getTransaction issues), you raise a ValueError, which turns an otherwise confirmed buy into a logical failure. That’s strict but could be painful if some RPC providers occasionally omit recent transaction history.

Consider a softer fallback: log a warning and proceed with the originally computed token_amount/token_price_sol when parsing fails, instead of raising, so successful on-chain buys don’t get marked as failures solely due to a metadata fetch glitch.

🧰 Tools
🪛 Ruff (0.14.5)

157-160: Abstract raise to an inner function

(TRY301)


157-160: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In src/trading/platform_aware.py around lines 134 to 160, the code currently
raises ValueError when tokens_raw or sol_spent is None, causing confirmed buys
to be treated as failures on transient RPC issues; change this to a soft
fallback: if tokens_raw or sol_spent is None (or if calling
_get_sol_destination/get_buy_transaction_details raises), log a warning that
includes the tx_signature and the None/exception details, do not raise, and
continue using the precomputed token_amount and token_price_sol (i.e., leave
existing values untouched); when tokens_raw and sol_spent are present keep the
current logic that computes actual_amount/actual_price and overrides
token_amount/token_price_sol and logs the info.

Comment on lines +589 to +594
# Execute sell with position quantity and entry price to avoid RPC delays
sell_result = await self.seller.execute(
token_info,
token_amount=position.quantity,
token_price=position.entry_price,
)
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

Use current market price for TP/SL exit sells instead of entry price

In _monitor_position_until_exit, you compute current_price each cycle to decide whether to exit, but the sell call uses position.entry_price:

sell_result = await self.seller.execute(
    token_info,
    token_amount=position.quantity,
    token_price=position.entry_price,
)

For stop‑loss or take‑profit exits, this can set min_sol_output based on a stale, higher entry price; on a loss this is very likely to exceed actual achievable SOL and cause the sell transaction to fail, leaving the position open despite the exit condition being met.

Apply this diff so the sell uses the same current_price you just fetched, avoiding extra RPC and aligning slippage with real market conditions:

-                    # Execute sell with position quantity and entry price to avoid RPC delays
-                    sell_result = await self.seller.execute(
-                        token_info,
-                        token_amount=position.quantity,
-                        token_price=position.entry_price,
-                    )
+                    # Execute sell with position quantity and current price (already fetched)
+                    sell_result = await self.seller.execute(
+                        token_info,
+                        token_amount=position.quantity,
+                        token_price=current_price,
+                    )
🤖 Prompt for AI Agents
In src/trading/universal_trader.py around lines 589 to 594, the sell call uses
position.entry_price which can be stale and cause min_sol_output to be too high
for TP/SL exits; change the call to pass the already-fetched current_price (the
price used to decide the exit) as token_price so slippage/min output aligns with
market, i.e., use current_price instead of position.entry_price when calling
self.seller.execute.

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.

Is the bot able to execute sell 5-7 seconds after buy transaction?

3 participants