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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bots/bot-sniper-1-geyser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ compute_units:
# buy: 100_000 # Buy operations (ATA creation + trading)
# sell: 60_000 # Sell operations (just trading)

# Account data size optimization (reduces CU cost and improves tx priority)
# Reduces CU cost from 16k to ~128 CU by limiting loaded account data.
# Default is 64MB (16k CU). Setting to 512KB significantly reduces overhead.
# Note: Savings don't show in "consumed CU" but improve tx priority/cost.
# Reference: https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
account_data_size: 512_000 # 512KB limit

# Filters for token selection
filters:
match_string: null # Only process tokens with this string in name/symbol
Expand Down
7 changes: 7 additions & 0 deletions bots/bot-sniper-2-logs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ compute_units:
# buy: 100_000 # Buy operations (ATA creation + trading)
# sell: 60_000 # Sell operations (just trading)

# Account data size optimization (reduces CU cost and improves tx priority)
# Reduces CU cost from 16k to ~128 CU by limiting loaded account data.
# Default is 64MB (16k CU). Setting to 512KB significantly reduces overhead.
# Note: Savings don't show in "consumed CU" but improve tx priority/cost.
# Reference: https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
account_data_size: 512_000 # 512KB limit

# Filters for token selection
filters:
match_string: null # Only process tokens with this string in name/symbol
Expand Down
7 changes: 7 additions & 0 deletions bots/bot-sniper-3-blocks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ compute_units:
# buy: 100_000 # Buy operations (ATA creation + trading)
# sell: 60_000 # Sell operations (just trading)

# Account data size optimization (reduces CU cost and improves tx priority)
# Reduces CU cost from 16k to ~128 CU by limiting loaded account data.
# Default is 64MB (16k CU). Setting to 512KB significantly reduces overhead.
# Note: Savings don't show in "consumed CU" but improve tx priority/cost.
# Reference: https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
account_data_size: 512_000 # 512KB limit

# Filters for token selection
filters:
match_string: null # Only process tokens with this string in name/symbol
Expand Down
7 changes: 7 additions & 0 deletions bots/bot-sniper-4-pp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ compute_units:
# buy: 100_000 # Buy operations (ATA creation + trading)
# sell: 60_000 # Sell operations (just trading)

# Account data size optimization (reduces CU cost and improves tx priority)
# Reduces CU cost from 16k to ~128 CU by limiting loaded account data.
# Default is 64MB (16k CU). Setting to 512KB significantly reduces overhead.
# Note: Savings don't show in "consumed CU" but improve tx priority/cost.
# Reference: https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
account_data_size: 512_000 # 512KB limit

# Filters for token selection
filters:
match_string: null # Only process tokens with this string in name/symbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

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

LAMPORTS_PER_SOL: Final[int] = 1_000_000_000
TOKEN_DECIMALS: Final[int] = 6
CURVE_ADDRESS: Final[str] = "YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address
CURVE_ADDRESS: Final[str] = (
"YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address
)

# Here and later all the discriminators are precalculated. See learning-examples/calculate_discriminator.py
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
Expand Down
45 changes: 44 additions & 1 deletion src/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import asyncio
import json
import struct
from typing import Any

import aiohttp
Expand All @@ -23,6 +24,35 @@
logger = get_logger(__name__)


def set_loaded_accounts_data_size_limit(bytes_limit: int) -> Instruction:
"""
Create SetLoadedAccountsDataSizeLimit instruction to reduce CU consumption.

By default, Solana transactions can load up to 64MB of account data,
costing 16k CU (8 CU per 32KB). Setting a lower limit reduces CU
consumption and improves transaction priority.

NOTE: CU savings are NOT visible in "consumed CU" metrics, which only
show execution CU. The 16k CU loaded accounts overhead is counted
separately for transaction priority/cost calculation.

Args:
bytes_limit: Max account data size in bytes (e.g., 512_000 = 512KB)

Returns:
Compute Budget instruction with discriminator 4

Reference:
https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit
"""
COMPUTE_BUDGET_PROGRAM = Pubkey.from_string(
"ComputeBudget111111111111111111111111111111"
)

data = struct.pack("<BI", 4, bytes_limit)
return Instruction(COMPUTE_BUDGET_PROGRAM, data, [])


class SolanaClient:
"""Abstraction for Solana RPC client operations."""

Expand Down Expand Up @@ -146,6 +176,7 @@ async def build_and_send_transaction(
max_retries: int = 3,
priority_fee: int | None = None,
compute_unit_limit: int | None = None,
account_data_size_limit: int | None = None,
) -> str:
"""
Send a transaction with optional priority fee and compute unit limit.
Expand All @@ -157,6 +188,8 @@ async def build_and_send_transaction(
max_retries: Maximum number of retry attempts.
priority_fee: Optional priority fee in microlamports.
compute_unit_limit: Optional compute unit limit. Defaults to 85,000 if not provided.
account_data_size_limit: Optional account data size limit in bytes (e.g., 512_000).
Reduces CU cost from 16k to ~128 CU. Must be first instruction.

Returns:
Transaction signature.
Expand All @@ -168,9 +201,19 @@ async def build_and_send_transaction(
)

# Add compute budget instructions if applicable
if priority_fee is not None or compute_unit_limit is not None:
if (
priority_fee is not None
or compute_unit_limit is not None
or account_data_size_limit is not None
):
fee_instructions = []

if account_data_size_limit is not None:
fee_instructions.append(
set_loaded_accounts_data_size_limit(account_data_size_limit)
)
logger.info(f"Account data size limit: {account_data_size_limit} bytes")

# Set compute unit limit (use provided value or default to 85,000)
cu_limit = compute_unit_limit if compute_unit_limit is not None else 85_000
fee_instructions.append(set_compute_unit_limit(cu_limit))
Expand Down
4 changes: 3 additions & 1 deletion src/platforms/letsbonk/curve_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ def _decode_pool_state_with_idl(self, data: bytes) -> dict[str, Any]:
"real_quote": decoded_pool_state.get("real_quote", 0),
"status": decoded_pool_state.get("status", 0),
"supply": decoded_pool_state.get("supply", 0),
"creator": decoded_pool_state.get("creator"), # Creator pubkey (as base58 string)
"creator": decoded_pool_state.get(
"creator"
), # Creator pubkey (as base58 string)
"base_vault": decoded_pool_state.get("base_vault"), # Base vault pubkey
"quote_vault": decoded_pool_state.get("quote_vault"), # Quote vault pubkey
}
Expand Down
12 changes: 11 additions & 1 deletion src/platforms/letsbonk/event_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,17 @@ def get_account_key(index):
base_vault = get_account_key(8) # base_vault account
quote_vault = get_account_key(9) # quote_vault account

if not all([creator, global_config, platform_config, pool_state, base_mint, base_vault, quote_vault]):
if not all(
[
creator,
global_config,
platform_config,
pool_state,
base_mint,
base_vault,
quote_vault,
]
):
logger.debug(
f"Missing required accounts: creator={creator}, global_config={global_config}, "
f"platform_config={platform_config}, pool_state={pool_state}, base_mint={base_mint}, "
Expand Down
24 changes: 16 additions & 8 deletions src/trading/platform_aware.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ async def execute(self, token_info: TokenInfo) -> TradeResult:
compute_unit_limit=instruction_builder.get_buy_compute_unit_limit(
self._get_cu_override("buy", token_info.platform)
),
account_data_size_limit=self._get_cu_override(
"account_data_size", token_info.platform
),
)

success = await self.client.confirm_transaction(tx_signature)
Expand Down Expand Up @@ -154,17 +157,17 @@ def _get_pool_address(

def _get_cu_override(self, operation: str, platform: Platform) -> int | None:
"""Get compute unit override from configuration.

Args:
operation: "buy" or "sell"
platform: Trading platform (unused - each config is platform-specific)

Returns:
CU override value if configured, None otherwise
"""
if not self.compute_units:
return None

# Just check for operation override (buy/sell)
return self.compute_units.get(operation)

Expand Down Expand Up @@ -234,7 +237,9 @@ async def execute(self, token_info: TokenInfo) -> TradeResult:
(expected_sol_output * (1 - self.slippage)) * LAMPORTS_PER_SOL
)

logger.info(f"Selling {token_balance_decimal} tokens on {token_info.platform.value}")
logger.info(
f"Selling {token_balance_decimal} tokens on {token_info.platform.value}"
)
logger.info(f"Expected SOL output: {expected_sol_output:.8f} SOL")
logger.info(
f"Minimum SOL output (with {self.slippage * 100:.1f}% slippage): {min_sol_output / LAMPORTS_PER_SOL:.8f} SOL"
Expand Down Expand Up @@ -266,6 +271,9 @@ async def execute(self, token_info: TokenInfo) -> TradeResult:
compute_unit_limit=instruction_builder.get_sell_compute_unit_limit(
self._get_cu_override("sell", token_info.platform)
),
account_data_size_limit=self._get_cu_override(
"account_data_size", token_info.platform
),
)

success = await self.client.confirm_transaction(tx_signature)
Expand Down Expand Up @@ -309,16 +317,16 @@ def _get_pool_address(

def _get_cu_override(self, operation: str, platform: Platform) -> int | None:
"""Get compute unit override from configuration.

Args:
operation: "buy" or "sell"
platform: Trading platform (unused - each config is platform-specific)

Returns:
CU override value if configured, None otherwise
"""
if not self.compute_units:
return None

# Just check for operation override (buy/sell)
return self.compute_units.get(operation)
return self.compute_units.get(operation)