diff --git a/.env b/.env index 7e9f218..1f066e6 100644 --- a/.env +++ b/.env @@ -27,4 +27,5 @@ OPENAI_API_KEY=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 # aider --model groq/llama3-70b-8192 # List models available from Groq -# aider --models groq/ \ No newline at end of file +# aider --models groq/ +SUBSCRIPTION_ID='2217755' diff --git a/.gitignore b/.gitignore index 707c6db..1b5f1a3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ agent-mAId/output.wav agent-mAId/build/* agent-mAId/dist/main.exe agent-mAId/output.wav -.node-persist/storage/* \ No newline at end of file +.node-persist/storage/* +logs/* +crypto/sol/.env diff --git a/crypto/sol/.env b/crypto/sol/.env index b3f203b..f0b63f2 100644 --- a/crypto/sol/.env +++ b/crypto/sol/.env @@ -1,4 +1,7 @@ -SOLANA_NET_URL="wss://api.mainnet-beta.solana.com" + +SOLANA_WS_URL="wss://api.mainnet-beta.solana.com" +SOLANA_WS_URL2="wss://mainnet.rpcpool.com" +SOLANA_HTTP_URL="https://api.mainnet-beta.solana.com" DEVELOPER_CHAT_ID="777826553" # Niki's # FOLLOWED_WALLET="9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN" diff --git a/crypto/sol/.env.example b/crypto/sol/.env.example new file mode 100644 index 0000000..e949837 --- /dev/null +++ b/crypto/sol/.env.example @@ -0,0 +1,15 @@ + +SOLANA_WS_URL="wss://api.mainnet-beta.solana.com" +SOLANA_WS_URL2="wss://mainnet.rpcpool.com" +SOLANA_HTTP_URL="https://api.mainnet-beta.solana.com" +DEVELOPER_CHAT_ID="777826553" +# Niki's +# FOLLOWED_WALLET="9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN" +# My test Brave sync wallet +FOLLOWED_WALLET="7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV" + +TELEGRAM_BOT_TOKEN="6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw" +DISPLAY_CURRENCY=USD + +YOUR_WALLET="65nzyZXTLC81MthTo52a2gRJjqryTizWVqpK2fDKLye5" +PK={} \ No newline at end of file diff --git a/crypto/sol/app.py b/crypto/sol/app.py index dd88d3e..cde34d0 100644 --- a/crypto/sol/app.py +++ b/crypto/sol/app.py @@ -3,40 +3,108 @@ import websockets import json from flask import Flask, render_template, request, jsonify from solana.rpc.async_api import AsyncClient -from solana.rpc.commitment import Confirmed +from solana.transaction import Signature +from solana.rpc.websocket_api import connect +from solana.rpc.types import TokenAccountOpts, TxOpts +from solana.rpc.commitment import Confirmed, Processed +from solana.transaction import Transaction +from base64 import b64decode +import base58 +from solders.rpc.requests import GetTransaction +from solders.signature import Signature from solders.pubkey import Pubkey +from solders.keypair import Keypair +from solders.transaction import VersionedTransaction +from solders.transaction import Transaction +from solders.message import Message +from solders.instruction import Instruction +from solders.hash import Hash +from solders.instruction import CompiledInstruction +from solders import message +from jupiter_python_sdk.jupiter import Jupiter, Jupiter_DCA from dexscreener import DexscreenerClient from telegram import Bot from telegram.constants import ParseMode import datetime import logging -from solana.rpc.websocket_api import connect -from solana.rpc.async_api import AsyncClient -from solana.rpc.commitment import Confirmed -from solana.rpc.types import TokenAccountOpts import base64 import os -from dotenv import load_dotenv +from dotenv import load_dotenv,set_key import aiohttp from typing import List, Dict - - +import requests +import threading +import re +from typing import List, Dict, Any, Tuple load_dotenv() app = Flask(__name__) -# Use the production Solana RPC endpoint -solana_client = AsyncClient("https://api.mainnet-beta.solana.com") -dexscreener_client = DexscreenerClient() +ENV_FILE = '.env' + +async def save_subscription_id(subscription_id): + # storing subscription id in .env file disabled + #set_key(ENV_FILE, "SUBSCRIPTION_ID", str(subscription_id)) + logger.info(f"Saved subscription ID: {subscription_id}") + +async def load_subscription_id(): + subscription_id = os.getenv("SUBSCRIPTION_ID") + return int(subscription_id) if subscription_id else None + + + +# Function to find the latest log file +def get_latest_log_file(): + log_dir = './logs' + try: + # files = [f for f in os.listdir(log_dir) if os.path.isfile(os.path.join(log_dir, f))] + # filter files mask log_20241005_004103_143116.json + files = [f for f in os.listdir(log_dir) if os.path.isfile(os.path.join(log_dir, f)) and f.startswith('log_')] + + latest_file = max(files, key=lambda x: os.path.getctime(os.path.join(log_dir, x))) + return os.path.join(log_dir, latest_file) + except Exception as e: + logging.error(f"Error fetching latest log file: {e}") + return None + +# Flask route to retry processing the last log +@app.route('/retry-last-log', methods=['GET']) +def retry_last_log(): + latest_log_file = get_latest_log_file() + if not latest_log_file: + return jsonify({"error": "No log files found"}), 404 + + try: + with open(latest_log_file, 'r') as f: + log = json.load(f) + + # await process_log(log) + # Run the asynchronous process_log function + asyncio.run(process_log(log)) + return jsonify({"status": "Log dump processed successfully"}), 200 + + except Exception as e: + logging.error(f"Error processing log dump: {e}") + return jsonify({"error": "Failed to process log"}), 500 + + + # Configuration DEVELOPER_CHAT_ID = os.getenv("DEVELOPER_CHAT_ID") FOLLOWED_WALLET = os.getenv("FOLLOWED_WALLET") YOUR_WALLET = os.getenv("YOUR_WALLET") TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") -SOLANA_URL = os.getenv("SOLANA_NET_URL") +SOLANA_WS_URL = os.getenv("SOLANA_WS_URL") +SOLANA_HTTP_URL = os.getenv("SOLANA_HTTP_URL") DISPLAY_CURRENCY = os.getenv('DISPLAY_CURRENCY', 'USD') + +# Use the production Solana RPC endpoint +solana_client = AsyncClient(SOLANA_HTTP_URL) +dexscreener_client = DexscreenerClient() + + # Initialize Telegram Bot bot = Bot(token=TELEGRAM_BOT_TOKEN) @@ -44,46 +112,18 @@ bot = Bot(token=TELEGRAM_BOT_TOKEN) TOKEN_ADDRESSES = { "SOL": "So11111111111111111111111111111111111111112", "USDC": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "TARD": "4nfn86ssbv7wiqcsw7bpvn46k24jhe334fudtyxhp1og" + "TARD": "4nfn86ssbv7wiqcsw7bpvn46k24jhe334fudtyxhp1og", } async def send_telegram_message(message): try: await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML) logging.info(f"Telegram message sent: {message}") + # logging.info(f"Telegram message dummy sent: {message}") except Exception as e: logging.error(f"Error sending Telegram message: {str(e)}") -# async def get_token_prices(token_addresses: List[str]) -> Dict[str, float]: -# url = "https://api.coingecko.com/api/v3/simple/token_price/solana" -# params = { -# "contract_addresses": ",".join(token_addresses), -# "vs_currencies": DISPLAY_CURRENCY.lower() -# } -# prices = {} - -# async with aiohttp.ClientSession() as session: -# async with session.get(url, params=params) as response: -# if response.status == 200: -# data = await response.json() -# for address, price_info in data.items(): -# if DISPLAY_CURRENCY.lower() in price_info: -# prices[address] = price_info[DISPLAY_CURRENCY.lower()] -# else: -# logging.error(f"Failed to get token prices. Status: {response.status}") - -# # For tokens not found in CoinGecko, try to get price from a DEX or set a default value -# missing_tokens = set(token_addresses) - set(prices.keys()) -# for token in missing_tokens: -# # You might want to implement a fallback method here, such as: -# # prices[token] = await get_price_from_dex(token) -# # For now, we'll set a default value -# prices[token] = 0.0 -# logging.warning(f"Price not found for token {token}. Setting to 0.") - -# return prices - async def get_token_prices(token_addresses: List[str]) -> Dict[str, float]: coingecko_prices = await get_prices_from_coingecko(token_addresses) @@ -165,62 +205,70 @@ async def get_sol_price_from_dexscreener() -> float: prices = await get_prices_from_dexscreener([sol_address]) return prices.get(sol_address, 0.0) -async def get_sol_price() -> float: - url = f"https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies={DISPLAY_CURRENCY.lower()}" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - if response.status == 200: - data = await response.json() - return data['solana'][DISPLAY_CURRENCY.lower()] - else: - logging.error(f"Failed to get SOL price. Status: {response.status}") - return None - -async def convert_balances_to_currency(balances, token_prices, sol_price): - converted_balances = {} - for token, amount in balances.items(): - if token == 'SOL': - converted_balances[token] = amount * sol_price - elif token in token_prices: - converted_balances[token] = amount * token_prices[token] - else: - converted_balances[token] = None # Price not available - logging.warning(f"Price not available for token {token}") - return converted_balances - - - -async def get_token_balance(wallet_address, token_address): +async def get_token_balance_rpc(wallet_address, token_address): + url = SOLANA_HTTP_URL + headers = {"Content-Type": "application/json"} + data = { + "jsonrpc": "2.0", + "id": 1, + "method": "getTokenAccountsByOwner", + "params": [ + wallet_address, + { + "mint": token_address + }, + { + "encoding": "jsonParsed" + } + ] + } try: - response = await solana_client.get_token_accounts_by_owner( - Pubkey.from_string(wallet_address), - {"mint": Pubkey.from_string(token_address)} - ) - if response['result']['value']: - balance = await solana_client.get_token_account_balance( - response['result']['value'][0]['pubkey'] - ) - amount = float(balance['result']['value']['uiAmount']) - logging.debug(f"Balance for {token_address} in {wallet_address}: {amount}") - return amount + response = requests.post(url, headers=headers, data=json.dumps(data)) + response.raise_for_status() # Raises an error for bad responses + accounts = response.json() + + if 'result' in accounts and accounts['result']['value']: + first_account = accounts['result']['value'][0]['pubkey'] + balance_data = { + "jsonrpc": "2.0", + "id": 1, + "method": "getTokenAccountBalance", + "params": [ + first_account + ] + } + balance_response = requests.post(url, headers=headers, data=json.dumps(balance_data)) + balance_response.raise_for_status() + balance = balance_response.json() + + if 'result' in balance and 'value' in balance['result']: + amount = float(balance['result']['value']['uiAmount']) + logging.debug(f"Balance for {token_address} in {wallet_address}: {amount}") + return amount + else: + logging.debug(f"No balance found for {token_address} in {wallet_address}") + return 0 else: logging.debug(f"No account found for {token_address} in {wallet_address}") return 0 - except Exception as e: - logging.error(f"Error getting balance for {token_address} in {wallet_address}: {str(e)}") + except requests.exceptions.RequestException as e: + logging.error(f"Error getting balance for {token_address} in {wallet_address}: {str(e)} \r\n {e}") return 0 + -class SolanaEncoder(json.JSONEncoder): - def default(self, obj): - if hasattr(obj, '__dict__'): - return obj.__dict__ - return str(obj) +async def get_token_name(mint_address): + try: + token_info = await solana_client.get_account_info_json_parsed(Pubkey.from_string(mint_address)) + if token_info.value and 'symbol' in token_info.value: + return token_info.value['symbol'] + except Exception as e: + logging.error(f"Error fetching token name for {mint_address}: {str(e)}") + return None async def get_wallet_balances(wallet_address): balances = {} - token_addresses = [] logging.info(f"Getting balances for wallet: {wallet_address}") try: @@ -239,96 +287,80 @@ async def get_wallet_balances(wallet_address): info = parsed_data['info'] if isinstance(info, dict) and 'mint' in info and 'tokenAmount' in info: mint = info['mint'] - amount = float(info['tokenAmount']['uiAmount']) + #amount = float(info['tokenAmount']['amount']) / (10 ** info['tokenAmount']['decimals']) + amount = float(info['tokenAmount']['amount'])/10**info['tokenAmount']['decimals'] + decimals = info['tokenAmount']['decimals'] if amount > 0: - token_addresses.append(mint) - balances[mint] = amount - logging.debug(f"Balance for {mint}: {amount}") + token_name = await get_token_name(mint) or 'Unknown' + balances[mint] = { + 'name': token_name, + 'address': mint, + 'amount': amount, + 'decimals': decimals + } + logging.debug(f"Balance for {token_name} ({mint}): {amount}") else: logging.warning(f"Unexpected data format for account: {account}") sol_balance = await solana_client.get_balance(Pubkey.from_string(wallet_address)) if sol_balance.value is not None: - balances['SOL'] = sol_balance.value / 1e9 + balances['SOL'] = { + 'name': 'SOL', + 'address': 'SOL', + 'amount': sol_balance.value / 1e9 + } else: logging.warning(f"SOL balance response missing for wallet: {wallet_address}") except Exception as e: logging.error(f"Error getting wallet balances: {str(e)}") - return balances, token_addresses + return balances -async def get_converted_balances(wallet_address): - balances, token_addresses = await get_wallet_balances(wallet_address) - token_prices = await get_token_prices(token_addresses) - sol_price = await get_sol_price() - converted_balances = await convert_balances_to_currency(balances, token_prices, sol_price) +async def convert_balances_to_currency(balances , sol_price): + converted_balances = {} + for address, info in balances.items(): + converted_balance = info.copy() # Create a copy of the original info + if info['name'] == 'SOL': + converted_balance['value'] = info['amount'] * sol_price + elif address in TOKEN_PRICES: + converted_balance['value'] = info['amount'] * TOKEN_PRICES[address] + else: + converted_balance['value'] = None # Price not available + logging.warning(f"Price not available for token {info['name']} ({address})") + converted_balances[address] = converted_balance return converted_balances - - -async def send_initial_wallet_states(followed_wallet, your_wallet): - followed_balances = await get_converted_balances(followed_wallet) - your_balances = await get_converted_balances(your_wallet) - - message = f"Initial Wallet States (Non-zero balances in {DISPLAY_CURRENCY}):\n\n" - message += f"Followed Wallet ({followed_wallet}):\n" - for token, amount in followed_balances.items(): - if amount and amount > 0: - message += f"{token}: {amount:.2f}\n" - - message += f"\nYour Wallet ({your_wallet}):\n" - for token, amount in your_balances.items(): - if amount and amount > 0: - message += f"{token}: {amount:.2f}\n" - - message += "\nMonitored Tokens:\n" - # Add monitored tokens logic here if needed - - await bot.send_message(chat_id=CHAT_ID, text=message) - - - -async def get_non_zero_token_balances(wallet_address): - non_zero_balances = {} - logging.info(f"Getting non-zero balances for wallet: {wallet_address}") - for token, address in TOKEN_ADDRESSES.items(): - balance = await get_token_balance(wallet_address, address) - if balance > 0: - non_zero_balances[token] = address - logging.debug(f"Non-zero balance for {token}: {balance}") - return non_zero_balances - - + async def list_initial_wallet_states(): - global TOKEN_ADDRESSES, FOLLOWED_WALLET_VALUE, YOUR_WALLET_VALUE + global TOKEN_ADDRESSES, FOLLOWED_WALLET_VALUE, YOUR_WALLET_VALUE, TOKEN_PRICES - followed_wallet_balances, followed_token_addresses = await get_wallet_balances(FOLLOWED_WALLET) - your_wallet_balances, your_token_addresses = await get_wallet_balances(YOUR_WALLET) + followed_wallet_balances = await get_wallet_balances(FOLLOWED_WALLET) + your_wallet_balances = await get_wallet_balances(YOUR_WALLET) - all_token_addresses = list(set(followed_token_addresses + your_token_addresses)) - token_prices = await get_token_prices(all_token_addresses) + all_token_addresses = list(set(followed_wallet_balances.keys()) | set(your_wallet_balances.keys())) + TOKEN_PRICES = await get_token_prices(all_token_addresses) sol_price = await get_sol_price() - followed_converted_balances = await convert_balances_to_currency(followed_wallet_balances, token_prices, sol_price) - your_converted_balances = await convert_balances_to_currency(your_wallet_balances, token_prices, sol_price) + followed_converted_balances = await convert_balances_to_currency(followed_wallet_balances, sol_price) + your_converted_balances = await convert_balances_to_currency(your_wallet_balances, sol_price) - TOKEN_ADDRESSES = {token: amount for token, amount in {**followed_converted_balances, **your_converted_balances}.items() if amount is not None and amount > 0} - logging.info(f"Monitoring balances for tokens: {TOKEN_ADDRESSES.keys()}") + TOKEN_ADDRESSES = {address: info for address, info in {**followed_converted_balances, **your_converted_balances}.items() if info['value'] is not None and info['value'] > 0} + logging.info(f"Monitoring balances for tokens: {[info['name'] for info in TOKEN_ADDRESSES.values()]}") followed_wallet_state = [] FOLLOWED_WALLET_VALUE = 0 - for token, amount in followed_converted_balances.items(): - if amount is not None and amount > 0: - followed_wallet_state.append(f"{token}: {amount:.2f} {DISPLAY_CURRENCY}") - FOLLOWED_WALLET_VALUE += amount + for address, info in followed_converted_balances.items(): + if info['value'] is not None and info['value'] > 0: + followed_wallet_state.append(f"{info['name']} ({address}): {info['value']:.2f} {DISPLAY_CURRENCY}") + FOLLOWED_WALLET_VALUE += info['value'] your_wallet_state = [] YOUR_WALLET_VALUE = 0 - for token, amount in your_converted_balances.items(): - if amount is not None and amount > 0: - your_wallet_state.append(f"{token}: {amount:.2f} {DISPLAY_CURRENCY}") - YOUR_WALLET_VALUE += amount + for address, info in your_converted_balances.items(): + if info['value'] is not None and info['value'] > 0: + your_wallet_state.append(f"{info['name']} ({address}): {info['value']:.2f} {DISPLAY_CURRENCY}") + YOUR_WALLET_VALUE += info['value'] message = ( f"Initial Wallet States (All balances in {DISPLAY_CURRENCY}):\n\n" @@ -339,126 +371,495 @@ async def list_initial_wallet_states(): f"{chr(10).join(your_wallet_state)}\n" f"Total Value: {YOUR_WALLET_VALUE:.2f} {DISPLAY_CURRENCY}\n\n" f"Monitored Tokens:\n" - f"{', '.join(TOKEN_ADDRESSES.keys())}" + f"{', '.join([info['name'] for info in TOKEN_ADDRESSES.values()])}" ) logging.info(message) await send_telegram_message(message) +async def get_swap_transaction_details(tx_signature_str): + t = await solana_client.get_transaction(Signature.from_string(tx_signature_str), max_supported_transaction_version=0) + try: + parsed_result = { + "order_id": None, + "token_in": None, + "token_out": None, + "amount_in": 0, + "amount_out": 0, + "amount_in_USD": 0, + "amount_out_USD": 0, + "percentage_swapped": 0 + } + + # Extract log messages for order_id and swap details. we have that in the log hook + # log_messages = t.value.meta.log_messages + # for log in log_messages: + # if "order_id" in log: + # parsed_result["order_id"] = log.split(":")[1].strip() + # break + + instructions = t.value.transaction.transaction.message.instructions + # Parse the swap instruction to extract token addresses, amounts, and types + for instruction in instructions: + if isinstance(instruction, CompiledInstruction): + if instruction.program_id == Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"): + parsed_info = instruction.parsed.info + mint = parsed_info["mint"] + amount = float(parsed_info["tokenAmount"]["amount"]) / (10 ** parsed_info["tokenAmount"]["decimals"]) + + # Determine token in and token out based on balances + if parsed_result["token_in"] is None and amount > 0: + parsed_result["token_in"] = mint + parsed_result["amount_in"] = amount + elif parsed_result["token_out"] is None: + parsed_result["token_out"] = mint + parsed_result["amount_out"] = amount + + # Calculate USD values if token is USDC + if parsed_result["token_in"] == "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": + parsed_result["amount_in_USD"] = parsed_result["amount_in"] + if parsed_result["token_out"] == "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": + parsed_result["amount_out_USD"] = parsed_result["amount_out"] + + # Calculate percentage swapped + if parsed_result["amount_in"] > 0 and parsed_result["amount_out"] > 0: + parsed_result["percentage_swapped"] = (parsed_result["amount_out"] / parsed_result["amount_in"]) * 100 + + return parsed_result + + except Exception as e: + logging.error(f"Error fetching transaction details: {e}") + + return None + +async def get_transaction_details_with_retry(transaction_id, retry_delay = 11, max_retries = 11): + # wait for the transaction to be confirmed + # await async_client.wait_for_confirmation(Signature.from_string(transaction_id)) + # qwery every 5 seconds for the transaction details untill not None or 30 seconds + for _ in range(max_retries): + try: + tx_details = await get_transaction_details_rpc(transaction_id) + if tx_details is not None: + break + except Exception as e: + logging.error(f"Error fetching transaction details: {e}") + logging.info(f"({_} of {max_retries}) Waiting for transaction details for {transaction_id}") + await asyncio.sleep(retry_delay) + return tx_details + + +async def get_transaction_details_rpc(tx_signature, readfromDump=False): + + try: + if readfromDump and os.path.exists('./logs/transation_details.json'): + with open('./logs/transation_details.json', 'r') as f: # trump_swap_tr_details + transaction_details = json.load(f) + return transaction_details + else: + transaction_details = await solana_jsonrpc("getTransaction", [tx_signature]) + with open('./logs/transation_details.json', 'w') as f: + json.dump(transaction_details, f, indent=2) + + if transaction_details is None: + logging.error(f"Error fetching transaction details for {tx_signature}") + return None + + # Initialize default result structure + parsed_result = { + "order_id": None, + "token_in": None, + "token_out": None, + "amount_in": 0, + "amount_out": 0, + "amount_in_USD": 0, + "amount_out_USD": 0, + "percentage_swapped": 0 + } + + # Extract order_id from logs + log_messages = transaction_details.get("meta", {}).get("logMessages", []) + for log in log_messages: + if "order_id" in log: + parsed_result["order_id"] = log.split(":")[2].strip() + break + + # Extract token transfers from innerInstructions + inner_instructions = transaction_details.get('meta', {}).get('innerInstructions', []) + for instruction_set in inner_instructions: + for instruction in instruction_set.get('instructions', []): + if instruction.get('program') == 'spl-token' and instruction.get('parsed', {}).get('type') == 'transferChecked': + info = instruction['parsed']['info'] + mint = info['mint'] + amount = float(info['tokenAmount']['amount']) / 10 ** info['tokenAmount']['decimals'] # Adjust for decimals + + # Determine which token is being swapped in and out based on zero balances + if parsed_result["token_in"] is None and amount > 0: + parsed_result["token_in"] = mint + parsed_result["amount_in"] = amount + + + if parsed_result["token_in"] is None or parsed_result["token_out"] is None: + # if we've failed to extract token_in and token_out from the transaction details, try a second method + inner_instructions = transaction_details.get('meta', {}).get('innerInstructions', []) + transfers = [] + + for instruction_set in inner_instructions: + for instruction in instruction_set.get('instructions', []): + if instruction.get('program') == 'spl-token' and instruction.get('parsed', {}).get('type') in ['transfer', 'transferChecked']: + info = instruction['parsed']['info'] + amount = float(info['amount']) if 'amount' in info else float(info['tokenAmount']['amount']) + decimals = info['tokenAmount']['decimals'] if 'tokenAmount' in info else 0 + adjusted_amount = amount / (10 ** decimals) + # adjusted_amount = float(info["amount"]) / (10 ** (info["tokenAmount"]["decimals"] if 'tokenAmount' in info else 0)) + transfers.append({ + 'mint': info.get('mint'), + 'amount': adjusted_amount, + 'source': info['source'], + 'destination': info['destination'] + }) + + # Identify token_in and token_out + if len(transfers) >= 2: + parsed_result["token_in"] = transfers[0]['mint'] + parsed_result["amount_in"] = transfers[0]['amount'] + parsed_result["token_out"] = transfers[-1]['mint'] + parsed_result["amount_out"] = transfers[-1]['amount'] + + # If mint is not provided, query the Solana network for the account data + if parsed_result["token_in"] is None or parsed_result["token_out"] is None: + #for transfer in transfers: + # do only first and last transfer + for transfer in [transfers[0], transfers[-1]]: + if transfer['mint'] is None: + # Query the Solana network for the account data + account_data_result = await solana_jsonrpc("getAccountInfo", [transfer['source']]) + + if 'value' in account_data_result and 'data' in account_data_result['value']: + account_data_value = account_data_result['value'] + account_data_data = account_data_value['data'] + if 'parsed' in account_data_data and 'info' in account_data_data['parsed']: + account_data_info = account_data_data['parsed']['info'] + if 'mint' in account_data_info: + transfer['mint'] = account_data_info['mint'] + if parsed_result["token_in"] is None: + parsed_result["token_in"] = transfer['mint'] + parsed_result["amount_in"] = transfer['amount']/10**6 + elif parsed_result["token_out"] is None: + parsed_result["token_out"] = transfer['mint'] + parsed_result["amount_out"] = transfer['amount']/10**6 + + pre_balalnces = transaction_details.get('meta', {}).get('preTokenBalances', []) + for balance in pre_balalnces: + if balance['mint'] == parsed_result["token_in"] and balance['owner'] == FOLLOWED_WALLET: + parsed_result["before_source_balance"] = float(balance['uiTokenAmount']['amount']) / 10 ** balance['uiTokenAmount']['decimals'] + break + + + # Calculate percentage swapped + try: + if parsed_result["amount_in"] > 0 and 'before_source_balance' in parsed_result and parsed_result["before_source_balance"] > 0: + parsed_result["percentage_swapped"] = (parsed_result["amount_in"] / parsed_result["before_source_balance"]) * 100 + except Exception as e: + logging.error(f"Error calculating percentage swapped: {e}") + + return parsed_result + + except requests.exceptions.RequestException as e: + print("Error fetching transaction details:", e) + + +async def solana_jsonrpc(method, params = None, jsonParsed = True): + # target json example: + # data = { + # "jsonrpc": "2.0", + # "id": 1, + # "method": "getTransaction", + # "params": [ + # tx_signature, + # { + # "encoding": "jsonParsed", + # "maxSupportedTransactionVersion": 0 + # } + # ] + # } + + data = { + "jsonrpc": "2.0", + "id": 1, + "method": method, + "params": params or [] + } + data["params"].append({"maxSupportedTransactionVersion": 0}) + if jsonParsed: + data["params"][1]["encoding"] = "jsonParsed" + + + try: + # url = 'https://solana.drpc.org' + response = requests.post(SOLANA_HTTP_URL, headers={"Content-Type": "application/json"}, data=json.dumps(data)) + response.raise_for_status() # Raises an error for bad responses + result = response.json() + if not 'result' in result or 'error' in result: + print("Error fetching data from Solana RPC:", result) + return None + return result['result'] + except Exception as e: + logging.error(f"Error fetching data from Solana RPC: {e}") + return None + + +async def save_log(log): + try: + os.makedirs('./logs', exist_ok=True) + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") + filename = f"./logs/log_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(log, f, indent=2) + except Exception as e: + logging.error(f"Error saving RPC log: {e}") + + + +async def process_log(log_result): + if log_result['value']['err']: + return + + logs = log_result['value']['logs'] + try: + # Detect swap operations in logs + swap_operations = ['Program log: Instruction: Swap', 'Program log: Instruction: Swap2', 'Program log: Instruction: SwapExactAmountIn'] + + if any(op in logs for op in swap_operations): + # Save the log to a file + await save_log(log_result) + tx_signature_str = log_result['value']['signature'] + + before_source_balance = 0 + source_token_change = 0 + + tr_details = { + "order_id": None, + "token_in": None, + "token_out": None, + "amount_in": 0, + "amount_out": 0, + "amount_in_USD": 0, + "amount_out_USD": 0, + "percentage_swapped": 0 + } + i = 0 + while i < len(logs): + log_entry = logs[i] + + # Check if we found the 'order_id' + if tr_details["order_id"] is None and "order_id" in log_entry: + # Extract the order_id + tr_details["order_id"] = log_entry.split(":")[-1].strip() + tr_details["token_in"] = logs[i + 1].split(":")[-1].strip() + tr_details["token_out"] = logs[i + 2].split(":")[-1].strip() + + # Look for the token change amounts after tokens have been found + if "source_token_change" in log_entry: + parts = log_entry.split(", ") + for part in parts: + if "source_token_change" in part: + tr_details["amount_in"] = float(part.split(":")[-1].strip()) / 10 ** 6 # Assuming 6 decimals + elif "destination_token_change" in part: + tr_details["amount_out"] = float(part.split(":")[-1].strip()) / 10 ** 6 # Assuming 6 decimals + + i += 1 + + # calculatte percentage swapped by digging before_source_balance, source_token_change and after_source_balance + + # "Program log: before_source_balance: 19471871, before_destination_balance: 0, amount_in: 19471871, expect_amount_out: 770877527, min_return: 763168752", + # "Program log: after_source_balance: 0, after_destination_balance: 770570049", + # "Program log: source_token_change: 19471871, destination_token_change: 770570049", + if "before_source_balance" in log_entry: + parts = log_entry.split(", ") + for part in parts: + if "before_source_balance" in part: + before_source_balance = float(part.split(":")[-1].strip()) / 10 ** 6 + if "source_token_change" in log_entry: + parts = log_entry.split(", ") + for part in parts: + if "source_token_change" in part: + source_token_change = float(part.split(":")[-1].strip()) / 10 ** 6 + + + # GET DETAILS FROM TRANSACTION IF NOT FOUND IN LOGS + if tr_details["token_in"] is None or tr_details["token_out"] is None or tr_details["amount_in"] == 0 or tr_details["amount_out"] == 0: + logging.warning("Incomplete swap details found in logs. Getting details from transaction") + tr_details = await get_transaction_details_info(tx_signature_str, logs) + # onlt needed if no details got + if before_source_balance > 0 and source_token_change > 0: + tr_details["percentage_swapped"] = (source_token_change / before_source_balance) * 100 + #dirty fix for percentage > 100 (decimals 9 but expecting 6) + if tr_details["percentage_swapped"] > 100: + tr_details["percentage_swapped"] = tr_details["percentage_swapped"] / 1000 + + + message_text = ( + f"Swap detected:\n" + f"Token In: {tr_details['token_in']}\n" + f"Token Out: {tr_details['token_out']}\n" + f"Amount In USD: {tr_details['amount_in_USD']}\n" + f"Percentage Swapped: {tr_details['percentage_swapped']:.2f}%" + ) + + await send_telegram_message(message_text) + await follow_move(tr_details) + + except Exception as e: + logging.error(f"Error processing log: {e}") + + + + # "Program log: Instruction: Swap2", + # "Program log: order_id: 13985890735038016", + # "Program log: AbrMJWfDVRZ2EWCQ1xSCpoVeVgZNpq1U2AoYG98oRXfn", source + # "Program log: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", target + # "Program log: before_source_balance: 58730110139, before_destination_balance: 202377778, amount_in: 58730110139, expect_amount_out: 270109505, min_return: 267408410", + # "Program log: after_source_balance: 0, after_destination_balance: 472509072", + # "Program log: source_token_change: 58730110139, destination_token_change: 270131294", + +async def get_transaction_details_info(tx_signature_str: str, logs: List[str]) -> Dict[str, Any]: + + try: + tr_info = await get_swap_transaction_details(tx_signature_str) + except Exception as e: + logging.error(f"Error fetching swap transaction details: {e}") + + tr_info = await get_transaction_details_with_retry(tx_signature_str) + + # Fetch token prices + token_prices = await get_token_prices([tr_info['token_in'], tr_info['token_out']]) + + # Calculate USD values + tr_info['amount_in_usd'] = tr_info['amount_in'] * token_prices.get(tr_info['token_in'], 0) + tr_info['amount_out_usd'] = tr_info['amount_out'] * token_prices.get(tr_info['token_out'], 0) + + # Calculate the percentage of the source balance that was swapped; ToDo: fix decimals for percentage + tr_info['percentage_swapped'] = (tr_info['amount_in'] / tr_info['before_source_balance']) * 100 if tr_info['before_source_balance'] > 0 else 50 + return tr_info + +def _get_pre_balance(transaction_details: Dict[str, Any], token: str) -> float: + pre_balances = transaction_details.get('meta', {}).get('preTokenBalances', []) + for balance in pre_balances: + if balance['mint'] == token: + return float(balance['uiTokenAmount']['amount']) + return 0.0 + async def follow_move(move): - followed_balances = await get_wallet_balances(FOLLOWED_WALLET) your_balances = await get_wallet_balances(YOUR_WALLET) - - if move['token'] not in followed_balances or move['token'] not in your_balances: - logging.error(f"Invalid token: {move['token']}") + your_balance_info = next((balance for balance in your_balances.values() if balance['address'] == move['token_in']), None) + + if not your_balance_info: + msg = f"Move Failed:\nNo balance found for token {move['token_in']}" + logging.warning(msg) + await send_telegram_message(msg) return - followed_balance = followed_balances[move['token']] - your_balance = your_balances[move['token']] + your_balance = your_balance_info['amount'] + token_name = your_balance_info['name'] - proportion = your_balance / followed_balance if followed_balance > 0 else 0 - amount_to_swap = move['amount'] * proportion + # move["percentage_swapped"] = (move["amount_out"] / move["amount_in"]) * 100 + # Calculate the amount to swap based on the same percentage as the followed move + amount_to_swap = your_balance * (move['percentage_swapped'] / 100) + + # # always get 99% of the amount to swap + # amount_to_swap = amount_to_swap * 0.99 if your_balance >= amount_to_swap: - # Implement actual swap logic here - pair = dexscreener_client.get_token_pair("solana", move['token']) - price = float(pair['priceUsd']) - received_amount = amount_to_swap * price + try: + private_key = Keypair.from_bytes(base58.b58decode(os.getenv("PK"))) + async_client = AsyncClient(SOLANA_WS_URL) + jupiter = Jupiter(async_client, private_key) + + # Convert to lamports + # if decimals is 6, then amount = amount * 1e6; if 9, then amount = amount * 1e9 + amount = int(amount_to_swap * 10**your_balance_info['decimals']) + transaction_data = await jupiter.swap( + input_mint=move['token_in'], + output_mint=move['token_out'], + amount=amount, + slippage_bps=100, # Increased to 1% + ) + raw_transaction = VersionedTransaction.from_bytes(base64.b64decode(transaction_data)) + signature = private_key.sign_message(message.to_bytes_versioned(raw_transaction.message)) + signed_txn = VersionedTransaction.populate(raw_transaction.message, [signature]) + opts = TxOpts(skip_preflight=False, preflight_commitment=Processed) + # send the transaction + result = await async_client.send_raw_transaction(txn=bytes(signed_txn), opts=opts) - message = ( - f"Move Followed:\n" - f"Swapped {amount_to_swap:.6f} {move['token']} " - f"for {received_amount:.6f} {move['to_token']}" - ) - logging.info(message) - await send_telegram_message(message) + transaction_id = json.loads(result.to_json())['result'] + print(f"Follow Transaction Sent: https://solscan.io/tx/{transaction_id}") + notification = ( + f"Move Initiated:\n (decimals: {your_balance_info['decimals']})\n" + f"Swapping {move['percentage_swapped']:.2f}% ({amount_to_swap}) {token_name} ({move['token_in']}) " + f"for {move['token_out']}" + f"\n\nTransaction: {transaction_id}" + ) + logging.info(notification) + await send_telegram_message(notification) + + tx_details = await get_transaction_details_with_retry(transaction_id) + + if tx_details is None: + logging.info(f"Failed to get transaction details for {transaction_id}") + notification = ( + f"Move Followed:\n" + f"Swapped {amount_to_swap:.6f} {token_name} ({move['token_in']}) " + f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n" + f"\n\nTransaction: {transaction_id}" + ) + + else: + output_token_info = your_balances.get(move['token_out'], {'name': 'Unknown'}) + output_token_name = output_token_info['name'] + + notification = ( + f"Move Followed:\n" + f"Swapped {amount_to_swap:.6f} {token_name} ({move['token_in']}) " + f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n" + f"for {tx_details['amount_out']:.6f} {output_token_name} ({move['token_out']})" + # f"Amount In USD: {tr_details['amount_in_USD']}\n" + f"\n\nTransaction: {transaction_id}" + ) + logging.info(notification) + await send_telegram_message(notification) + + except Exception as e: + error_message = f"Swap Follow Error:\n{str(e)}" + logging.error(error_message) + # await send_telegram_message(error_message) else: - message = ( - f"Move Failed:\n" - f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}" + msg = ( + f"Move Not Followed:\n" + f"Insufficient balance to swap {amount_to_swap:.6f} {token_name} ({move['token_in']})" ) - logging.warning(message) - await send_telegram_message(message) - followed_balances = await get_wallet_balances(FOLLOWED_WALLET) - your_balances = await get_wallet_balances(YOUR_WALLET) - - if move['token'] not in followed_balances or move['token'] not in your_balances: - logging.error(f"Invalid token: {move['token']}") - return + logging.warning(msg) + await send_telegram_message(msg) - followed_balance = followed_balances[move['token']] - your_balance = your_balances[move['token']] - - proportion = your_balance / followed_balance if followed_balance > 0 else 0 - amount_to_swap = move['amount'] * proportion - if your_balance >= amount_to_swap: - # Implement actual swap logic here - pair = dexscreener_client.get_token_pair("solana", move['token']) - price = float(pair['priceUsd']) - received_amount = amount_to_swap * price +# Helper functions (implement these according to your needs) + - message = ( - f"Move Followed:\n" - f"Swapped {amount_to_swap:.6f} {move['token']} " - f"for {received_amount:.6f} {move['to_token']}" - ) - logging.info(message) - await send_telegram_message(message) - else: - message = ( - f"Move Failed:\n" - f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}" - ) - logging.warning(message) - await send_telegram_message(message) async def on_logs(log): - print(f"Received log: {log}") - try: - if 'err' in log and log['err']: - return + logging.debug(f"Received log: {log}") + await process_log(log) - if 'value' in log and 'logs' in log['value']: - tx = log['value']['signature'] - logs = log['value']['logs'] - - # Fetch transaction details - tx_result = await solana_client.get_transaction(tx) - - if tx_result and 'result' in tx_result and tx_result['result']: - transaction = tx_result['result']['transaction'] - message = transaction['message'] - - for log_entry in logs: - if 'Program log: Instruction: Swap' in log_entry: - # Handle swap event - for instruction in message['instructions']: - if instruction['programId'] == TOKEN_ADDRESSES['SOL']: - # This is a token transfer - from_pubkey = instruction['accounts'][0] - to_pubkey = instruction['accounts'][1] - amount = int(instruction['data'], 16) / 1e9 # Convert lamports to SOL - - if from_pubkey == FOLLOWED_WALLET: - move = { - 'token': 'SOL', - 'amount': amount, - 'to_token': 'Unknown' # You might want to determine this based on the receiving address - } - await follow_move(move) - - # Send a Telegram message about the swap - message_text = f"Swap detected:\nFrom: {from_pubkey}\nTo: {to_pubkey}\nAmount: {amount} SOL" - await send_telegram_message(message_text) - else: - print(f"Unexpected log format: {log}") - except: - print(f"error processing RPC log") async def subscribe_to_wallet(): - uri = SOLANA_URL + SOLANA_ENDPOINTS = [ + "wss://api.mainnet-beta.solana.com", + "wss://solana-api.projectserum.com", + "wss://rpc.ankr.com/solana", + "wss://mainnet.rpcpool.com", + ] + uri = SOLANA_WS_URL # wss://api.mainnet-beta.solana.com reconnect_delay = 5 # Start with a 5-second delay max_reconnect_delay = 60 # Maximum delay of 60 seconds @@ -467,6 +868,8 @@ async def subscribe_to_wallet(): async with websockets.connect(uri) as websocket: logger.info("Connected to Solana websocket") + subscription_id = await load_subscription_id() + request = { "jsonrpc": "2.0", "id": 1, @@ -488,8 +891,13 @@ async def subscribe_to_wallet(): try: response = await websocket.recv() response_data = json.loads(response) + logger.debug(f"Received response: {response_data}") if 'result' in response_data: - logger.info(f"Subscription successful. Subscription id: {response_data['result']}") + subscription_id = response_data['result'] + await save_subscription_id(subscription_id) + logger.info(f"Subscription successful. Subscription id: {subscription_id}") + await send_telegram_message("Connected to Solana network. Watching for transactions now.") + elif 'params' in response_data: await on_logs(response_data['params']['result']) else: @@ -516,15 +924,31 @@ async def subscribe_to_wallet(): reconnect_delay = min(reconnect_delay * 2, max_reconnect_delay) -logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) + + async def main(): # Initialize logging logging.basicConfig(level=logging.DEBUG) - logging.basicConfig(level=logging.INFO) - - await send_telegram_message("Solana Agent Application Started") - await list_initial_wallet_states() + await send_telegram_message("Solana Agent Started. Connecting to mainnet...") + # await list_initial_wallet_states() await subscribe_to_wallet() +def run_flask(): + # Run Flask app without the reloader, so we can run the async main function + app.run(debug=False, port=3001, use_reloader=False) + if __name__ == '__main__': - asyncio.run(main()) \ No newline at end of file + # Start Flask in a separate thread + flask_thread = threading.Thread(target=run_flask) + flask_thread.start() + + # Create an event loop for the async tasks + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + # Start Flask in a separate thread + flask_thread = threading.Thread(target=run_flask) + flask_thread.start() + + # Run the async main function + asyncio.run(main()) diff --git a/crypto/sol/example.rpc.json b/crypto/sol/example.rpc.json index d0c6adf..b7d6f73 100644 --- a/crypto/sol/example.rpc.json +++ b/crypto/sol/example.rpc.json @@ -1,62 +1,136 @@ -{'context': {'slot': 293286129 - }, 'value': {'signature': '2J59rHhGhf8kAvCqyW2bG69KvDxKbh3jKUTPrYvZhgzSk5vAoqTy5NucAW7JC2tK5vTfCLV5UZB6WbwGBbWaBKQ3', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [ - 1 - ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ - 1 - ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 1 - ]', 'Program log: Instruction: SharedAccountsRoute', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 2 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 127535 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ - 2 - ]', 'Program log: ray_log: A6XA5awOAAAAAAAAAAAAAAACAAAAAAAAAKXA5awOAAAAXs+S5bOKQADQulmTTwIAAKoshgAAAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 96679 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 89053 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30407 of 113869 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 2 - ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 81088 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ - 2 - ]', 'Program log: ray_log: A6oshgAAAAAAAAAAAAAAAAABAAAAAAAAABrmohwAAAAAkAJTsDYEjAC3BYLQ/AIAALJPjoAYAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 54460 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 46743 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30542 of 71786 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 2 - ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 38870 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 2 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 34592 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 118828 of 147615 compute units', 'Program return: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 sk+OgBgAAAA=', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success' - ] +[ + {'context': {'slot': 293286129 + }, 'value': {'signature': '2J59rHhGhf8kAvCqyW2bG69KvDxKbh3jKUTPrYvZhgzSk5vAoqTy5NucAW7JC2tK5vTfCLV5UZB6WbwGBbWaBKQ3', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 1 + ]', 'Program log: Instruction: SharedAccountsRoute', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 127535 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ + 2 + ]', 'Program log: ray_log: A6XA5awOAAAAAAAAAAAAAAACAAAAAAAAAKXA5awOAAAAXs+S5bOKQADQulmTTwIAAKoshgAAAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 96679 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 89053 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30407 of 113869 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 81088 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ + 2 + ]', 'Program log: ray_log: A6oshgAAAAAAAAAAAAAAAAABAAAAAAAAABrmohwAAAAAkAJTsDYEjAC3BYLQ/AIAALJPjoAYAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 54460 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 46743 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30542 of 71786 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 38870 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 34592 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 118828 of 147615 compute units', 'Program return: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 sk+OgBgAAAA=', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success' + ] + } } -} error logging: {'context': {'slot': 293286129 - }, 'value': {'signature': '2J59rHhGhf8kAvCqyW2bG69KvDxKbh3jKUTPrYvZhgzSk5vAoqTy5NucAW7JC2tK5vTfCLV5UZB6WbwGBbWaBKQ3', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [ - 1 - ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ - 1 - ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 1 - ]', 'Program log: Instruction: SharedAccountsRoute', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 2 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 127535 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ - 2 - ]', 'Program log: ray_log: A6XA5awOAAAAAAAAAAAAAAACAAAAAAAAAKXA5awOAAAAXs+S5bOKQADQulmTTwIAAKoshgAAAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 96679 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 89053 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30407 of 113869 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 2 - ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 81088 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ - 2 - ]', 'Program log: ray_log: A6oshgAAAAAAAAAAAAAAAAABAAAAAAAAABrmohwAAAAAkAJTsDYEjAC3BYLQ/AIAALJPjoAYAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 54460 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 3 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 46743 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30542 of 71786 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ - 2 - ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 38870 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ - 2 - ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 34592 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 118828 of 147615 compute units', 'Program return: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 sk+OgBgAAAA=', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success' - ] + }, 'value': {'signature': '2J59rHhGhf8kAvCqyW2bG69KvDxKbh3jKUTPrYvZhgzSk5vAoqTy5NucAW7JC2tK5vTfCLV5UZB6WbwGBbWaBKQ3', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 1 + ]', 'Program log: Instruction: SharedAccountsRoute', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 127535 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ + 2 + ]', 'Program log: ray_log: A6XA5awOAAAAAAAAAAAAAAACAAAAAAAAAKXA5awOAAAAXs+S5bOKQADQulmTTwIAAKoshgAAAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 96679 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 89053 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30407 of 113869 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 81088 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ + 2 + ]', 'Program log: ray_log: A6oshgAAAAAAAAAAAAAAAAABAAAAAAAAABrmohwAAAAAkAJTsDYEjAC3BYLQ/AIAALJPjoAYAAAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 54460 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 46743 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30542 of 71786 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 38870 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 34592 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 118828 of 147615 compute units', 'Program return: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 sk+OgBgAAAA=', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success' + ] + } + }, + {'context': {'slot': 293354340 + }, 'value': {'signature': '3JB8qbmk9Ybb713jpUzHVdYGy3DskfCpeGhoF8PSSdcUCPuqPW9tnuSFSXTHCjZJdLWdL4WxGYLnsaPKAY2NEEJf', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [ + 1 + ]', 'Program log: CreateIdempotent', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: GetAccountDataSize', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1569 of 283888 compute units', 'Program return: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA pQAAAAAAAAA=', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 11111111111111111111111111111111 invoke [ + 2 + ]', 'Program 11111111111111111111111111111111 success', 'Program log: Initialize the associated token account', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: InitializeImmutableOwner', 'Program log: Please upgrade to SPL Token 2022 for immutable owner support', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1405 of 277301 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: InitializeAccount3', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4188 of 273419 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 20345 of 289293 compute units', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 1 + ]', 'Program log: Instruction: SharedAccountsRoute', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 251725 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c invoke [ + 2 + ]', 'Program log: Instruction: Swap', 'Program log: AMM: { + "p": 9xERPkyJuPBnffKX2SswG6r25sJMSyD4hTDqgm8d5QoV + }', 'Program log: Oracle: { + "a": 221333189.0427, + "b": 1638750694, + "c": 25000000000, + "d": 221266809 + }', 'Program log: Amount: { + "in": 100000000, + "out": 45171716, + "impact": 0.03 + }', 'Program log: TotalFee: { + "fee": 20000, + "percent": 0.02 + }', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 178442 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: MintTo', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4492 of 171397 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 164519 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c consumed 82968 of 239120 compute units', 'Program 2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 153831 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc invoke [ + 2 + ]', 'Program log: Instruction: Swap', 'Program log: fee_growth: 9627455162', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 116303 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 108598 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc consumed 45050 of 146166 compute units', 'Program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 98801 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [ + 2 + ]', 'Program log: ray_log: A9KGPyoAAAAAAAAAAAAAAAABAAAAAAAAACbVb0cAAAAAL7y1hK9GXQBgVYsi7QMAAKRp3ADpAwAA', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 72171 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 64454 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 30544 of 89499 compute units', 'Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [ + 2 + ]', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 471 of 56581 compute units', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 2 + ]', 'Program log: Instruction: Transfer', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 52303 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 consumed 222659 of 268948 compute units', 'Program return: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 pGncAOkDAAA=', 'Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success' + ] + } } -} \ No newline at end of file +] + + + + +// new +DEBUG:websockets.client:< TEXT '{"jsonrpc":"2.0","method":"logsNotification","p...subscription":1135969}}' [5193 bytes] +Received log: {'context': {'slot': 293522789}, 'value': {'signature': '5YLbNKHr4wRVgYtTHqpWD7HshHWgbxfpXaAU8nKWvTjZvwnNZ5kMqDSuQtzwd6eHZoDKaWkq3FcXbTNe3VhHZT2X', 'err': None, 'logs': ['Program ComputeBudget111111111111111111111111111111 invoke [1]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [1]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program 11111111111111111111111111111111 invoke [1]', 'Program 11111111111111111111111111111111 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]', 'Program log: Instruction: InitializeAccount', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3443 of 291550 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 11111111111111111111111111111111 invoke [1]', 'Program 11111111111111111111111111111111 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]', 'Program log: Instruction: SyncNative', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3045 of 287957 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]', 'Program log: CreateIdempotent', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 5838 of 284912 compute units', 'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma invoke [1]', 'Program log: Instruction: Swap2', 'Program log: order_id: 13975709597114048', 'Program log: So11111111111111111111111111111111111111112', 'Program log: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'Program log: before_source_balance: 1000000, before_destination_balance: 410923, amount_in: 1000000, expect_amount_out: 136796, min_return: 135429', 'Program log: Dex::MeteoraDlmm amount_in: 1000000, offset: 0', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo invoke [2]', 'Program log: Instruction: Swap', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6238 of 205783 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6173 of 196112 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo invoke [3]', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo consumed 2136 of 186508 compute units', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo success', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo consumed 58606 of 241445 compute units', 'Program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo success', 'Program data: QMbN6CYIceINQEIPAAAAAAC5FgAAAAAAAA==', 'Program log: SwapEvent { dex: MeteoraDlmm, amount_in: 1000000, amount_out: 5817 }', 'Program log: Dsk3jzujuXbtA6TBCPwHVdN5LHWmyCqTNWTfbPZWTvDt', 'Program log: GXhNWTQvtXw7hHPrCwiDKex1rRr7g6zDui7hhGTb5XuJ', 'Program log: Dex::RaydiumClmmSwapV2 amount_in: 5817, offset: 18', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK invoke [2]', 'Program log: Instruction: SwapV2', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6173 of 94632 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6200 of 84589 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program data: QMbN6CYIceLKQf6Jla6jDAa3lvdi80E57zZ2sbaEVINfz/B6mnAddPTnnWLPabh8F9TFvyB2wHgkKjclvP1voW9r2pZM385W5rxgJ6Ti0iTAE+4ET3R0kukR1DtbTAHIU9ZrSlmyVtFIH3U7ixqCaqbGoeydkTdTUUCkOQYTvNcEdWHVAFRzK7kWAAAAAAAAAAAAAAAAAABcFgIAAAAAAAAAAAAAAAAAAQJ4BOyAPFnZBAAAAAAAAABM7IECAAAAAAAAAAAAAAAAWXsAAA==', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK consumed 87691 of 159628 compute units', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK success', 'Program data: QMbN6CYIceILuRYAAAAAAABcFgIAAAAAAA==', 'Program log: SwapEvent { dex: RaydiumClmmSwapV2, amount_in: 5817, amount_out: 136796 }', 'Program log: GXhNWTQvtXw7hHPrCwiDKex1rRr7g6zDui7hhGTb5XuJ', 'Program log: 5rYBCPjppwZntGzmqMUgC1NVnSc2JdeWXLbZHssE1UyY', 'Program log: after_source_balance: 0, after_destination_balance: 547719', 'Program log: source_token_change: 1000000, destination_token_change: 136796', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma consumed 216917 of 279074 compute units', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]', 'Program log: Instruction: CloseAccount', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2915 of 62157 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success']}} \ No newline at end of file diff --git a/crypto/sol/readme.md b/crypto/sol/readme.md index 6aaad30..d4c989d 100644 --- a/crypto/sol/readme.md +++ b/crypto/sol/readme.md @@ -4,6 +4,7 @@ To run this Python Solana agent: Install the required libraries: `pip install flask solana dexscreener python-telegram-bot asyncio base58 aiohttp` +pip install flask dexscreener python-telegram-bot aiohttp requests dotenv websockets solders solana Replace REPLACE_WITH_WALLET_ADDRESS with the wallet address you want to follow. Replace REPLACE_WITH_YOUR_WALLET_ADDRESS with your own wallet address. diff --git a/crypto/sol/transactionExample.json b/crypto/sol/transactionExample.json new file mode 100644 index 0000000..fbbc586 --- /dev/null +++ b/crypto/sol/transactionExample.json @@ -0,0 +1,193 @@ +{ + 'blockTime': 1728035375, +'meta': {'computeUnitsConsumed': 128099, +'err': None, 'fee': 97127, 'innerInstructions': [ + {'index': 4, 'instructions': [ + {'accounts': ['7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'A1BBtTYJd4i3xU8D6Tc2FzU6ZN4oXZWXKZnCxwbHXr8x', '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', '8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'BAxX7eMuSoxF8E4s1Xz5ukcLPHjvfC8Wv9JjSZci7tZ7', 'Gvjgjv63zcQxLbcG2iYF3vcJ2nudRLEffN6hoo8p6Ewy', 'HtsJ5S6K4NM2vXaWZ8k49JAggBgPGrryJ21ezdPBoUC6', 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr', 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'So11111111111111111111111111111111111111112', 'DeRo9XMsizey3KWBWS3LHomFC7ZDny1NkFMZUEHgyX8a', 'E6ef1fYRvZ8ejtbdwaK1CDNAgpJxkWZ2pQsL4jaShjNU', '423toqoYT7obTRx8qmwVAeYZfMFRxkwwDzRAwJBd86K3' + ], 'data': 'ASCsAbe1UnDmvdhmjnwFdPMzcfkrayiNaFZ61j7FiXYFR5MbydQDnzNL', 'programId': 'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK', 'stackHeight': 2 + }, + {'parsed': {'info': {'authority': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'destination': 'BAxX7eMuSoxF8E4s1Xz5ukcLPHjvfC8Wv9JjSZci7tZ7', 'mint': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'source': '8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'tokenAmount': {'amount': '200000000000', 'decimals': 8, 'uiAmount': 2000.0, 'uiAmountString': '2000' + } + }, 'type': 'transferChecked' + }, 'program': 'spl-token', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'stackHeight': 3 + }, + {'parsed': {'info': {'authority': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'destination': 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'mint': 'So11111111111111111111111111111111111111112', 'source': 'Gvjgjv63zcQxLbcG2iYF3vcJ2nudRLEffN6hoo8p6Ewy', 'tokenAmount': {'amount': '32677742', 'decimals': 9, 'uiAmount': 0.032677742, 'uiAmountString': '0.032677742' + } + }, 'type': 'transferChecked' + }, 'program': 'spl-token', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'stackHeight': 3 + } + ] + } + ], 'logMessages': ['Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program ComputeBudget111111111111111111111111111111 invoke [ + 1 + ]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program 11111111111111111111111111111111 invoke [ + 1 + ]', 'Program 11111111111111111111111111111111 success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 1 + ]', 'Program log: Instruction: InitializeAccount', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3443 of 216550 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma invoke [ + 1 + ]', 'Program log: Instruction: Swap2', 'Program log: order_id: 13978787684884160', 'Program log: Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'Program log: So11111111111111111111111111111111111111112', 'Program log: before_source_balance: 4000000000000, before_destination_balance: 0, amount_in: 200000000000, expect_amount_out: 32677742, min_return: 32350965', 'Program log: Dex: :RaydiumClmmSwapV2 amount_in: 200000000000, offset: 0', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK invoke [ + 2 + ]', 'Program log: Instruction: SwapV2', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6147 of 124210 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 3 + ]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6238 of 114194 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program data: QMbN6CYIceJeJCSmnTHyq+UhngUSm2ZraA736ls33e54BtTmtKTck18ss0Ijtep7x/QSQpelS9R/BLJJV8nYbkzHEgl6DWzmCqjvYwJou4+wNqo6UT4zu+LS3la0rmchxYO/qAt1QCxsBkiWENlC+V3iQ+9YxPA6ubSXncmJFhG2+Ianct8/LW6f8gEAAAAAAAAAAAAAAAAA0O2QLgAAAAAAAAAAAAAAAKdp93ci1pDdTQAAAAAAAAC1GvfaoAMAAAAAAAAAAAAAQFQBAA==', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK consumed 77136 of 178600 compute units', 'Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK success', 'Program data: QMbN6CYIceILANDtkC4AAABun/IBAAAAAA==', 'Program log: SwapEvent { dex: RaydiumClmmSwapV2, amount_in: 200000000000, amount_out: 32677742 + }', 'Program log: 8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'Program log: icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'Program log: after_source_balance: 3800000000000, after_destination_balance: 32677742', 'Program log: source_token_change: 200000000000, destination_token_change: 32677742', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma consumed 121291 of 213107 compute units', 'Program 6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma success', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [ + 1 + ]', 'Program log: Instruction: CloseAccount', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2915 of 91816 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success' + ], 'postBalances': [ + 93712473, + 0, + 2039280, + 72161280, + 72161280, + 1, + 255325042163, + 934087680, + 1, + 1141440, + 1705200, + 32092560, + 13641600, + 11637120, + 9935565889, + 2039280, + 1009200, + 1141440, + 521498880, + 617888496398, + 1141440 + ], 'postTokenBalances': [ + {'accountIndex': 2, 'mint': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'owner': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '3800000000000', 'decimals': 8, 'uiAmount': 38000.0, 'uiAmountString': '38000' + } + }, + {'accountIndex': 14, 'mint': 'So11111111111111111111111111111111111111112', 'owner': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '9933526609', 'decimals': 9, 'uiAmount': 9.933526609, 'uiAmountString': '9.933526609' + } + }, + {'accountIndex': 15, 'mint': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'owner': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '2460142552127709', 'decimals': 8, 'uiAmount': 24601425.52127709, 'uiAmountString': '24601425.52127709' + } + } + ], 'preBalances': [ + 61131858, + 0, + 2039280, + 72161280, + 72161280, + 1, + 255325042163, + 934087680, + 1, + 1141440, + 1705200, + 32092560, + 13641600, + 11637120, + 9968243631, + 2039280, + 1009200, + 1141440, + 521498880, + 617888496398, + 1141440 + ], 'preTokenBalances': [ + {'accountIndex': 2, 'mint': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'owner': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '4000000000000', 'decimals': 8, 'uiAmount': 40000.0, 'uiAmountString': '40000' + } + }, + {'accountIndex': 14, 'mint': 'So11111111111111111111111111111111111111112', 'owner': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '9966204351', 'decimals': 9, 'uiAmount': 9.966204351, 'uiAmountString': '9.966204351' + } + }, + {'accountIndex': 15, 'mint': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'owner': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'uiTokenAmount': {'amount': '2459942552127709', 'decimals': 8, 'uiAmount': 24599425.52127709, 'uiAmountString': '24599425.52127709' + } + } + ], 'rewards': [], 'status': {'Ok': None + } + }, 'slot': 293628136, 'transaction': {'message': {'accountKeys': [ + {'pubkey': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'signer': True, 'source': 'transaction', 'writable': True + }, + {'pubkey': 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'signer': False, 'source': 'transaction', 'writable': True + }, + {'pubkey': '8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'signer': False, 'source': 'transaction', 'writable': True + }, + {'pubkey': 'DeRo9XMsizey3KWBWS3LHomFC7ZDny1NkFMZUEHgyX8a', 'signer': False, 'source': 'transaction', 'writable': True + }, + {'pubkey': '423toqoYT7obTRx8qmwVAeYZfMFRxkwwDzRAwJBd86K3', 'signer': False, 'source': 'transaction', 'writable': True + }, + {'pubkey': '11111111111111111111111111111111', 'signer': False, 'source': 'transaction', 'writable': False + }, + {'pubkey': 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'signer': False, 'source': 'transaction', 'writable': False + }, + {'pubkey': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'signer': False, 'source': 'transaction', 'writable': False + }, + {'pubkey': 'ComputeBudget111111111111111111111111111111', 'signer': False, 'source': 'transaction', 'writable': False + }, + {'pubkey': '6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma', 'signer': False, 'source': 'transaction', 'writable': False + }, + {'pubkey': 'A1BBtTYJd4i3xU8D6Tc2FzU6ZN4oXZWXKZnCxwbHXr8x', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': 'HtsJ5S6K4NM2vXaWZ8k49JAggBgPGrryJ21ezdPBoUC6', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': 'E6ef1fYRvZ8ejtbdwaK1CDNAgpJxkWZ2pQsL4jaShjNU', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': 'Gvjgjv63zcQxLbcG2iYF3vcJ2nudRLEffN6hoo8p6Ewy', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': 'BAxX7eMuSoxF8E4s1Xz5ukcLPHjvfC8Wv9JjSZci7tZ7', 'signer': False, 'source': 'lookupTable', 'writable': True + }, + {'pubkey': 'SysvarRent111111111111111111111111111111111', 'signer': False, 'source': 'lookupTable', 'writable': False + }, + {'pubkey': 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', 'signer': False, 'source': 'lookupTable', 'writable': False + }, + {'pubkey': 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr', 'signer': False, 'source': 'lookupTable', 'writable': False + }, + {'pubkey': 'So11111111111111111111111111111111111111112', 'signer': False, 'source': 'lookupTable', 'writable': False + }, + {'pubkey': 'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK', 'signer': False, 'source': 'lookupTable', 'writable': False + } + ], 'addressTableLookups': [ + {'accountKey': '6ggEfUA8Y2BPkrg6yP2yjjEZRhnF6TyBG8q5tHQvdb2C', 'readonlyIndexes': [ + 1, + 4, + 5, + 41, + 13 + ], 'writableIndexes': [ + 27 + ] + }, + {'accountKey': '9kvMXWLiSBfbGxxrripJP4qZzHatdSbadYCAbwTexN28', 'readonlyIndexes': [], 'writableIndexes': [ + 17, + 18, + 12, + 13, + 15 + ] + } + ], 'instructions': [ + {'accounts': [], 'data': 'JPb7uH', 'programId': 'ComputeBudget111111111111111111111111111111', 'stackHeight': None + }, + {'accounts': [], 'data': '3VbWNdv4R831', 'programId': 'ComputeBudget111111111111111111111111111111', 'stackHeight': None + }, + {'parsed': {'info': {'base': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'lamports': 2039280, 'newAccount': 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'owner': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'seed': '1728035372645', 'source': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'space': 165 + }, 'type': 'createAccountWithSeed' + }, 'program': 'system', 'programId': '11111111111111111111111111111111', 'stackHeight': None + }, + {'parsed': {'info': {'account': 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'mint': 'So11111111111111111111111111111111111111112', 'owner': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'rentSysvar': 'SysvarRent111111111111111111111111111111111' + }, 'type': 'initializeAccount' + }, 'program': 'spl-token', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'stackHeight': None + }, + {'accounts': ['7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', '8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'So11111111111111111111111111111111111111112', 'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK', '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', '8Ggb8th8pZv7eJbRmwkoyrDbSvwLPvMQN7QuHwjm113z', 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'A1BBtTYJd4i3xU8D6Tc2FzU6ZN4oXZWXKZnCxwbHXr8x', '7LVHkVPVosXoWgH1PykWa5PjsfnyBviCdxC8ZUHJDy5U', 'BAxX7eMuSoxF8E4s1Xz5ukcLPHjvfC8Wv9JjSZci7tZ7', 'Gvjgjv63zcQxLbcG2iYF3vcJ2nudRLEffN6hoo8p6Ewy', 'HtsJ5S6K4NM2vXaWZ8k49JAggBgPGrryJ21ezdPBoUC6', 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr', 'Faf89929Ni9fbg4gmVZTca7eW6NFg877Jqn6MizT3Gvw', 'So11111111111111111111111111111111111111112', 'DeRo9XMsizey3KWBWS3LHomFC7ZDny1NkFMZUEHgyX8a', 'E6ef1fYRvZ8ejtbdwaK1CDNAgpJxkWZ2pQsL4jaShjNU', '423toqoYT7obTRx8qmwVAeYZfMFRxkwwDzRAwJBd86K3', '11111111111111111111111111111111' + ], 'data': '3sRDYcTeC9Apf4dwtR12F5tp2Pc4P5tDNYNtKqVRGjUmVJ4pEyVYZdwph4vkfkgyChXffRuiYRUXchviqNRXfd5ok6ub9PQK', 'programId': '6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma', 'stackHeight': None + }, + {'parsed': {'info': {'account': 'icV5K6iC8J7yyeP9YnJLH2jPRJYT7dBmzm844STxHkP', 'destination': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV', 'owner': '7QXGLRjvyFAmxdRaP9Wk18KwWTMfspF4Na2sr3o3PzxV' + }, 'type': 'closeAccount' + }, 'program': 'spl-token', 'programId': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'stackHeight': None + } + ], 'recentBlockhash': 'C2H99uuxKQRWeTngCRZoNHbQ2nDBAA2Uhk2USp7tDX9Z' + }, 'signatures': ['3PZLedLDQTb7ddor3XmwbQQycBCgFfMDXHWBRd9rK7gekWTHWetnEGEGpZb6XFep7EbMZFrJweM3q1tkGygZDthB' + ] + }, 'version': 0 +} \ No newline at end of file