Merge branch 'master' of http://git.d-popov.com/popov/ai-kevin
This commit is contained in:
commit
1888b8fbe6
@ -16,6 +16,9 @@ TELEGRAM_BOT_TOKEN="6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0"
|
|||||||
|
|
||||||
DISPLAY_CURRENCY=USD
|
DISPLAY_CURRENCY=USD
|
||||||
FOLLOW_AMOUNT=2
|
FOLLOW_AMOUNT=2
|
||||||
|
FOLLOW_AMOUNT=percentage
|
||||||
|
|
||||||
|
LIQUIDITY_TOKENS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v,So11111111111111111111111111111111111111112
|
||||||
|
|
||||||
# Niki's to Sync: [PROD]
|
# Niki's to Sync: [PROD]
|
||||||
FOLLOWED_WALLET="7keSmTZozjmuX66gd9GBSJYEHnMqsyutWpvuuKtXZKDH"
|
FOLLOWED_WALLET="7keSmTZozjmuX66gd9GBSJYEHnMqsyutWpvuuKtXZKDH"
|
||||||
|
@ -21,6 +21,7 @@ SOLANA_HTTP_URL = os.getenv("SOLANA_HTTP_URL")
|
|||||||
DISPLAY_CURRENCY = os.getenv('DISPLAY_CURRENCY', 'USD')
|
DISPLAY_CURRENCY = os.getenv('DISPLAY_CURRENCY', 'USD')
|
||||||
BOT_NAME = os.getenv("BOT_NAME")
|
BOT_NAME = os.getenv("BOT_NAME")
|
||||||
FOLLOW_AMOUNT = os.getenv('FOLLOW_AMOUNT', 'percentage')
|
FOLLOW_AMOUNT = os.getenv('FOLLOW_AMOUNT', 'percentage')
|
||||||
|
LIQUIDITY_TOKENS = os.getenv('LIQUIDITY_TOKENS', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v').split(',')
|
||||||
|
|
||||||
SOLANA_ENDPOINTS = [
|
SOLANA_ENDPOINTS = [
|
||||||
"wss://api.mainnet-beta.solana.com",
|
"wss://api.mainnet-beta.solana.com",
|
||||||
|
@ -25,11 +25,18 @@ from solana.rpc.async_api import AsyncClient
|
|||||||
from solana.rpc.types import TxOpts
|
from solana.rpc.types import TxOpts
|
||||||
from solana.rpc.commitment import Confirmed, Finalized, Processed
|
from solana.rpc.commitment import Confirmed, Finalized, Processed
|
||||||
|
|
||||||
from solders.transaction import VersionedTransaction
|
|
||||||
|
from solders.rpc.requests import GetTransaction
|
||||||
|
from solders.signature import Signature
|
||||||
|
from solders.pubkey import Pubkey
|
||||||
from solders.keypair import Keypair
|
from solders.keypair import Keypair
|
||||||
from solana.rpc.async_api import AsyncClient
|
from solders.transaction import VersionedTransaction
|
||||||
from solana.rpc.commitment import Processed
|
from solders.transaction import Transaction
|
||||||
from solana.rpc.types import TxOpts
|
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
|
from jupiter_python_sdk.jupiter import Jupiter
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -251,8 +258,8 @@ class SolanaAPI:
|
|||||||
await solana_ws.subscribe()
|
await solana_ws.subscribe()
|
||||||
|
|
||||||
if first_subscription:
|
if first_subscription:
|
||||||
await async_safe_call( self.on_initial_subscription, solana_ws.subscription_id)
|
|
||||||
first_subscription = False
|
first_subscription = False
|
||||||
|
await async_safe_call(self.on_initial_subscription, solana_ws.subscription_id)
|
||||||
|
|
||||||
await async_safe_call(self.on_bot_message,f"Solana mainnet connected ({solana_ws.subscription_id})...")
|
await async_safe_call(self.on_bot_message,f"Solana mainnet connected ({solana_ws.subscription_id})...")
|
||||||
|
|
||||||
@ -503,7 +510,7 @@ class SolanaAPI:
|
|||||||
if transfer['mint'] in DEX.TOKENS_INFO or 'decimals' not in DEX.TOKENS_INFO[transfer['mint']]:
|
if transfer['mint'] in DEX.TOKENS_INFO or 'decimals' not in DEX.TOKENS_INFO[transfer['mint']]:
|
||||||
await self.get_token_metadata_symbol(transfer['mint'])
|
await self.get_token_metadata_symbol(transfer['mint'])
|
||||||
# get actual prices
|
# get actual prices
|
||||||
current_price = await self.get_token_prices([transfer['mint']])
|
current_price = await DEX.get_token_prices([transfer['mint']])
|
||||||
|
|
||||||
if parsed_result["token_in"] is None:
|
if parsed_result["token_in"] is None:
|
||||||
parsed_result["token_in"] = transfer['mint']
|
parsed_result["token_in"] = transfer['mint']
|
||||||
@ -529,7 +536,9 @@ class SolanaAPI:
|
|||||||
parsed_result["percentage_swapped"] = (parsed_result["amount_in"] / parsed_result["before_source_balance"]) * 100
|
parsed_result["percentage_swapped"] = (parsed_result["amount_in"] / parsed_result["before_source_balance"]) * 100
|
||||||
else:
|
else:
|
||||||
# calculate based on total wallet value: FOLLOWED_WALLET_VALUE
|
# calculate based on total wallet value: FOLLOWED_WALLET_VALUE
|
||||||
parsed_result["percentage_swapped"] = (parsed_result["amount_in_USD"] / DEX.FOLLOWED_WALLET_VALUE) * 100
|
# division by 0
|
||||||
|
# parsed_result["percentage_swapped"] = (parsed_result["amount_in_USD"] / DEX.FOLLOWED_WALLET_VALUE) * 100
|
||||||
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error calculating percentage swapped: {e}")
|
logging.error(f"Error calculating percentage swapped: {e}")
|
||||||
|
|
||||||
@ -563,7 +572,7 @@ class SolanaAPI:
|
|||||||
# return float(balance['uiTokenAmount']['amount'])
|
# return float(balance['uiTokenAmount']['amount'])
|
||||||
# return 0.0
|
# return 0.0
|
||||||
|
|
||||||
async def get_transaction_details_with_retry(self, transaction_id, retry_delay = 5, max_retries = 16):
|
async def get_transaction_details_with_retry(self, transaction_id, retry_delay = 5, max_retries = 16, backoff= True):
|
||||||
# wait for the transaction to be confirmed
|
# wait for the transaction to be confirmed
|
||||||
# await async_client.wait_for_confirmation(Signature.from_string(transaction_id))
|
# await async_client.wait_for_confirmation(Signature.from_string(transaction_id))
|
||||||
# query every 5 seconds for the transaction details until not None or 30 seconds
|
# query every 5 seconds for the transaction details until not None or 30 seconds
|
||||||
@ -574,10 +583,11 @@ class SolanaAPI:
|
|||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error fetching transaction details: {e}")
|
logging.error(f"Error fetching transaction details: {e}")
|
||||||
retry_delay = retry_delay * 1.2
|
|
||||||
logging.info(f"({_} of {max_retries}) Waiting for transaction details for {transaction_id}. retry in {retry_delay} s.")
|
logging.info(f"({_} of {max_retries}) Waiting for transaction details for {transaction_id}. retry in {retry_delay} s.")
|
||||||
await asyncio.sleep(retry_delay)
|
await asyncio.sleep(retry_delay)
|
||||||
retry_delay *= 1.2
|
if backoff:
|
||||||
|
retry_delay = retry_delay * 1.2
|
||||||
return tx_details
|
return tx_details
|
||||||
|
|
||||||
|
|
||||||
@ -662,162 +672,174 @@ class SolanaAPI:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
async def follow_move(self,move):
|
async def follow_move(self,move):
|
||||||
your_balances = await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False)
|
try:
|
||||||
your_balance_info = next((balance for balance in your_balances.values() if balance['address'] == move['token_in']), None)
|
your_balances = await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False)
|
||||||
if your_balance_info is not None:
|
your_balance_info = next((balance for balance in your_balances.values() if balance['address'] == move['token_in']), None)
|
||||||
# Use the balance
|
if your_balance_info is not None:
|
||||||
print(f"Your balance: {your_balance_info['amount']} {move['symbol_in']}")
|
# Use the balance
|
||||||
else:
|
print(f"Your balance: {your_balance_info['amount']} {move['symbol_in']}")
|
||||||
print(f"No ballance found for {move['symbol_in']}. Skipping move.")
|
else:
|
||||||
await telegram_utils.send_telegram_message(f"No ballance found for {move['symbol_in']}. Skipping move.")
|
print(f"No ballance found for {move['symbol_in']}. Skipping move.")
|
||||||
return
|
await telegram_utils.send_telegram_message(f"No ballance found for {move['symbol_in']}. Skipping move.")
|
||||||
|
return
|
||||||
your_balance = your_balance_info['amount']
|
|
||||||
|
your_balance = your_balance_info['amount']
|
||||||
|
|
||||||
|
|
||||||
token_info = DEX.TOKENS_INFO.get(move['token_in'])
|
token_info = DEX.TOKENS_INFO.get(move['token_in'])
|
||||||
token_name_in = token_info.get('symbol') or await SAPI.get_token_metadata_symbol(move['token_in'])
|
token_name_in = token_info.get('symbol') or await SAPI.get_token_metadata_symbol(move['token_in'])
|
||||||
token_name_out = DEX.TOKENS_INFO[move['token_out']].get('symbol') or await SAPI.get_token_metadata_symbol(move['token_out'])
|
token_name_out = DEX.TOKENS_INFO[move['token_out']].get('symbol') or await SAPI.get_token_metadata_symbol(move['token_out'])
|
||||||
|
|
||||||
if not your_balance:
|
if not your_balance:
|
||||||
msg = f"<b>Move not followed:</b>\nNo balance found for token {move['symbol_in']}. Cannot follow move."
|
msg = f"<b>Move not followed:</b>\nNo balance found for token {move['symbol_in']}. Cannot follow move."
|
||||||
logging.warning(msg)
|
|
||||||
await telegram_utils.send_telegram_message(msg)
|
|
||||||
return
|
|
||||||
|
|
||||||
if FOLLOW_AMOUNT == 'percentage':
|
|
||||||
# Calculate the amount to swap based on the same percentage as the followed move
|
|
||||||
amount_to_swap = your_balance * (move['percentage_swapped'] / 100)
|
|
||||||
elif FOLLOW_AMOUNT == 'exact':
|
|
||||||
amount_to_swap = move['amount_in']
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
fixed_amount = float(FOLLOW_AMOUNT) # un USD
|
|
||||||
fixed_amount_in_token = fixed_amount / move["token_in_price"]
|
|
||||||
amount_to_swap = min(fixed_amount_in_token, your_balance)
|
|
||||||
except ValueError:
|
|
||||||
msg = f"<b>Move not followed:</b>\nInvalid FOLLOW_AMOUNT '{FOLLOW_AMOUNT}'. Must be 'percentage' or a number."
|
|
||||||
logging.warning(msg)
|
logging.warning(msg)
|
||||||
await telegram_utils.send_telegram_message(msg)
|
await telegram_utils.send_telegram_message(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
amount_to_swap = min(amount_to_swap, your_balance) # Ensure we're not trying to swap more than we have
|
if FOLLOW_AMOUNT == 'percentage':
|
||||||
|
# Calculate the amount to swap based on the same percentage as the followed move
|
||||||
decimals = token_info.get('decimals')
|
if move.get('percentage_swapped') is None:
|
||||||
# Convert to lamports
|
followed_ballances = await DEX.get_wallet_balances(FOLLOWED_WALLET, doGetTokenName=False)
|
||||||
# if decimals is 6, then amount = amount * 1e6; if 9, then amount = amount * 1e9
|
followed_ballance = next((balance for balance in followed_ballances.values() if balance['address'] == move['token_in']), None)
|
||||||
amount = int(amount_to_swap * 10**decimals)
|
if followed_ballance is not None:
|
||||||
amount = int(amount)
|
# Use the balance
|
||||||
logging.debug(f"Calculated amount in lamports: {amount}")
|
print(f"Followed balance: {followed_ballance['amount']} {move['symbol_in']}")
|
||||||
|
move['percentage_swapped'] = (move['amount_in']/followed_ballance['amount']) * 100
|
||||||
if your_balance < amount_to_swap: # should not happen
|
amount_to_swap = your_balance * (move['percentage_swapped'] / 100)
|
||||||
msg = (
|
|
||||||
f"<b>Warning:</b>\n"
|
|
||||||
f"Insufficient balance: {your_balance:.6f} {token_name_in}. We want to swap {amount_to_swap:.6f}\n({move['symbol_in']}, decimals {token_info.get('decimals')} amount {amount}).\n This will probably fail. But we will try anyway."
|
|
||||||
)
|
|
||||||
logging.warning(msg)
|
|
||||||
await telegram_utils.send_telegram_message(msg)
|
|
||||||
try:
|
|
||||||
|
|
||||||
try:
|
|
||||||
notification = (
|
|
||||||
f"<b>Initiating move:</b>\n"
|
|
||||||
f"Swapping {amount_to_swap:.2f} {token_name_in} for {token_name_out}"
|
|
||||||
+ (f" ({move['percentage_swapped']:.2f}%)" if 'percentage_swapped' in move else "")
|
|
||||||
)
|
|
||||||
# logging.info(notification)
|
|
||||||
# error_logger.info(notification)
|
|
||||||
# await telegram_utils.send_telegram_message(notification)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error sending notification: {e}")
|
|
||||||
|
|
||||||
if self.pk is None:
|
|
||||||
self.pk = await get_pk()
|
|
||||||
for retry in range(3):
|
|
||||||
try:
|
|
||||||
private_key = Keypair.from_bytes(base58.b58decode(self.pk))
|
|
||||||
async_client = AsyncClient(SOLANA_WS_URL)
|
|
||||||
jupiter = Jupiter(async_client, private_key)
|
|
||||||
transaction_data = await jupiter.swap(
|
|
||||||
input_mint=move['token_in'],
|
|
||||||
output_mint=move['token_out'],
|
|
||||||
amount=int(amount),
|
|
||||||
slippage_bps=300, # Increased to 3%
|
|
||||||
)
|
|
||||||
logging.info(f"Initiating move. Transaction data:\n {transaction_data}")
|
|
||||||
# error_logger.info(f"Initiating move. Transaction data:\n {transaction_data}")
|
|
||||||
raw_transaction = VersionedTransaction.from_bytes(base64.b64decode(transaction_data))
|
|
||||||
message = raw_transaction.message
|
|
||||||
|
|
||||||
signature = private_key.sign_message( bytes(message) )
|
|
||||||
# signature = private_key.sign_message(message.to_bytes_versioned())
|
|
||||||
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)
|
|
||||||
|
|
||||||
transaction_id = json.loads(result.to_json())['result']
|
|
||||||
print(f"Follow Transaction Sent: https://solscan.io/tx/{transaction_id}")
|
|
||||||
# append to notification
|
|
||||||
notification += f"\n\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>{transaction_id}</a>"
|
|
||||||
|
|
||||||
await telegram_utils.send_telegram_message(f"Follow Transaction Sent: {transaction_id}")
|
|
||||||
tx_details = await SAPI.get_transaction_details_with_retry(transaction_id)
|
|
||||||
|
|
||||||
if tx_details is not None:
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
logging.warning(f"Failed to get transaction details for {transaction_id}. Probably transaction failed. Retrying again...")
|
#fallback to 100 USD
|
||||||
await asyncio.sleep(3)
|
amount_to_swap = 100
|
||||||
except Exception as e:
|
|
||||||
error_message = f"<b>Move Failed:</b>\n{str(e)}</b>\n{transaction_data}</b>\n{move}"
|
|
||||||
logging.error(error_message)
|
|
||||||
# log the errors to /logs/errors.log
|
|
||||||
# error_logger.error(error_message)
|
|
||||||
# error_logger.exception(e)
|
|
||||||
await telegram_utils.send_telegram_message(error_message)
|
|
||||||
amount = int(amount * 0.75)
|
|
||||||
|
|
||||||
await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if tx_details is None:
|
|
||||||
logging.info(f"Failed to get transaction details for {transaction_id}")
|
|
||||||
notification = (
|
|
||||||
f"<b>Move Followed, failed to get transaction details.</b>\n"
|
|
||||||
f"Swapped {amount_to_swap:.6f} {token_name_in} ({move['token_in']}) "
|
|
||||||
f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n"
|
|
||||||
f"\n\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>{transaction_id}</a>"
|
|
||||||
# log_successful_swap ()
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
notification = (
|
amount_to_swap = your_balance * (move['percentage_swapped'] / 100)
|
||||||
f"<b>Move Followed:</b>\n"
|
elif FOLLOW_AMOUNT == 'exact':
|
||||||
f"Swapped {amount_to_swap:.6f} {token_name_in} ({move['symbol_in']}) "
|
amount_to_swap = move['amount_in']
|
||||||
f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n"
|
|
||||||
f"for {tx_details['amount_out']:.2f} {token_name_out}"
|
|
||||||
# f"Amount In USD: {tr_details['amount_in_USD']}\n"
|
|
||||||
f"\n\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>{transaction_id}</a>"
|
|
||||||
)
|
|
||||||
logging.info(notification)
|
|
||||||
await telegram_utils.send_telegram_message(notification)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error sending notification: {e}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
error_message = f"<b>Swap Follow Error:</b>\n{str(e)}"
|
|
||||||
logging.error(error_message)
|
|
||||||
# log the errors to /logs/errors.log
|
|
||||||
# error_logger.error(error_message)
|
|
||||||
# error_logger.exception(e)
|
|
||||||
# if error_message contains 'Program log: Error: insufficient funds'
|
|
||||||
if 'insufficient funds' in error_message:
|
|
||||||
await telegram_utils.send_telegram_message("Insufficient funds. Cannot follow move. Please check your balance.")
|
|
||||||
else:
|
else:
|
||||||
await telegram_utils.send_telegram_message(error_message)
|
try:
|
||||||
|
fixed_amount = float(FOLLOW_AMOUNT) # un USD
|
||||||
|
fixed_amount_in_token = fixed_amount / move["token_in_price"]
|
||||||
|
amount_to_swap = min(fixed_amount_in_token, your_balance)
|
||||||
|
except ValueError:
|
||||||
|
msg = f"<b>Move not followed:</b>\nInvalid FOLLOW_AMOUNT '{FOLLOW_AMOUNT}'. Must be 'percentage' or a number."
|
||||||
|
logging.warning(msg)
|
||||||
|
await telegram_utils.send_telegram_message(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
amount_to_swap = min(amount_to_swap, your_balance) # Ensure we're not trying to swap more than we have
|
||||||
|
|
||||||
|
decimals = token_info.get('decimals')
|
||||||
|
# Convert to lamports
|
||||||
|
# if decimals is 6, then amount = amount * 1e6; if 9, then amount = amount * 1e9
|
||||||
|
amount_lamports = int(amount_to_swap * 10**decimals)
|
||||||
|
logging.debug(f"Calculated amount in lamports: {amount_lamports}")
|
||||||
|
|
||||||
|
if your_balance < amount_to_swap: # should not happen
|
||||||
|
msg = (
|
||||||
|
f"<b>Warning:</b>\n"
|
||||||
|
f"Insufficient balance: {your_balance:.6f} {token_name_in}. We want to swap {amount_to_swap:.6f}\n({move['symbol_in']}, decimals {token_info.get('decimals')} amount {amount_lamports}).\n This will probably fail. But we will try anyway."
|
||||||
|
)
|
||||||
|
logging.warning(msg)
|
||||||
|
await telegram_utils.send_telegram_message(msg)
|
||||||
|
try:
|
||||||
|
|
||||||
|
try:
|
||||||
|
notification = (
|
||||||
|
f"<b>Initiating move:</b>\n"
|
||||||
|
f"Swapping {amount_to_swap:.2f} {token_name_in} for {token_name_out}"
|
||||||
|
+ (f" ({move['percentage_swapped']:.2f}%)" if 'percentage_swapped' in move else "")
|
||||||
|
)
|
||||||
|
# logging.info(notification)
|
||||||
|
# error_logger.info(notification)
|
||||||
|
# await telegram_utils.send_telegram_message(notification)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error sending notification: {e}")
|
||||||
|
|
||||||
|
if self.pk is None:
|
||||||
|
self.pk = await get_pk()
|
||||||
|
for retry in range(3):
|
||||||
|
try:
|
||||||
|
private_key = Keypair.from_bytes(base58.b58decode(self.pk))
|
||||||
|
async_client = AsyncClient(SOLANA_WS_URL)
|
||||||
|
jupiter = Jupiter(async_client, private_key)
|
||||||
|
transaction_data = await jupiter.swap(
|
||||||
|
input_mint=move['token_in'],
|
||||||
|
output_mint=move['token_out'],
|
||||||
|
amount=amount_lamports,
|
||||||
|
slippage_bps=300, # Increased to 3%
|
||||||
|
)
|
||||||
|
logging.info(f"Initiating move. Transaction data:\n {transaction_data}")
|
||||||
|
# error_logger.info(f"Initiating move. Transaction data:\n {transaction_data}")
|
||||||
|
raw_transaction = VersionedTransaction.from_bytes(base64.b64decode(transaction_data))
|
||||||
|
# message = raw_transaction.message
|
||||||
|
|
||||||
|
# signature = private_key.sign_message( bytes(message) )
|
||||||
|
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)
|
||||||
|
|
||||||
|
transaction_id = json.loads(result.to_json())['result']
|
||||||
|
notification = f"Follow Transaction Sent:\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>swapping {amount_to_swap:.2f} {token_name_in}</a>"
|
||||||
|
logging.info(notification)
|
||||||
|
await telegram_utils.send_telegram_message(notification)
|
||||||
|
tx_details = await SAPI.get_transaction_details_with_retry(transaction_id, retry_delay=5, max_retries=6, backoff=False)
|
||||||
|
|
||||||
|
if tx_details is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.warning(f"Failed to get transaction details for {transaction_id}.\n Probably transaction failed. Retrying again...")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
except Exception as e:
|
||||||
|
decoded_data = ''# base64.b64decode(transaction_data)
|
||||||
|
error_message = f"<b>Move Failed:</b>\n{str(e)}</b>\n{decoded_data}</b>\n{move}"
|
||||||
|
logging.error(error_message)
|
||||||
|
# log the errors to /logs/errors.log
|
||||||
|
# error_logger.error(error_message)
|
||||||
|
# error_logger.exception(e)
|
||||||
|
await telegram_utils.send_telegram_message(error_message)
|
||||||
|
amount = int(amount * 0.9)
|
||||||
|
|
||||||
|
await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if tx_details is None:
|
||||||
|
logging.info(f"Failed to get transaction details for {transaction_id}")
|
||||||
|
notification = (
|
||||||
|
f"<b>Move Followed, failed to get transaction details.</b>\n"
|
||||||
|
f"Swapped {amount_to_swap:.6f} {token_name_in} ({move['token_in']}) "
|
||||||
|
f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n"
|
||||||
|
f"\n\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>solscan.io</a>"
|
||||||
|
# log_successful_swap ()
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
notification = (
|
||||||
|
f"<b>Move Followed:</b>\n"
|
||||||
|
f"Swapped {amount_to_swap:.6f} {token_name_in} ({move['symbol_in']}) "
|
||||||
|
f"(same {move['percentage_swapped']:.2f}% as followed wallet)\n"
|
||||||
|
f"for {tx_details['amount_out']:.2f} {token_name_out}"
|
||||||
|
# f"Amount In USD: {tr_details['amount_in_USD']}\n"
|
||||||
|
f"\n\n<b>Transaction:</b> <a href='https://solscan.io/tx/{transaction_id}'>solscan.io</a>"
|
||||||
|
)
|
||||||
|
logging.info(notification)
|
||||||
|
await telegram_utils.send_telegram_message(notification)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error sending notification: {e}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"<b>Swap Follow Error:</b>\n{str(e)}"
|
||||||
|
logging.error(error_message)
|
||||||
|
# log the errors to /logs/errors.log
|
||||||
|
# error_logger.error(error_message)
|
||||||
|
# error_logger.exception(e)
|
||||||
|
# if error_message contains 'Program log: Error: insufficient funds'
|
||||||
|
if 'insufficient funds' in error_message:
|
||||||
|
await telegram_utils.send_telegram_message("Insufficient funds. Cannot follow move. Please check your balance.")
|
||||||
|
else:
|
||||||
|
await telegram_utils.send_telegram_message(error_message)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error following move: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from flask import Flask, jsonify, request, render_template, redirect, url_for
|
from flask import Flask, jsonify, request, render_template, redirect, url_for
|
||||||
# from flask_oauthlib.client import OAuth
|
# from flask_oauthlib.client import OAuth
|
||||||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
||||||
import secrets
|
import secrets
|
||||||
import json
|
import json
|
||||||
|
# from crypto.sol.config import LIQUIDITY_TOKENS
|
||||||
|
from config import LIQUIDITY_TOKENS
|
||||||
|
|
||||||
from modules import storage, utils, SolanaAPI
|
from modules import storage, utils, SolanaAPI
|
||||||
from modules.utils import async_safe_call
|
from modules.utils import async_safe_call
|
||||||
import os
|
import os
|
||||||
@ -98,8 +106,10 @@ def init_app(tr_handler=None):
|
|||||||
with open( os.path.join(SolanaAPI.root_path, 'logs', f'wh_{current_time}.json') , 'w') as f:
|
with open( os.path.join(SolanaAPI.root_path, 'logs', f'wh_{current_time}.json') , 'w') as f:
|
||||||
json.dump(request_data, f)
|
json.dump(request_data, f)
|
||||||
|
|
||||||
process_wh(request_data)
|
# await process_wh(request_data)
|
||||||
return jsonify({"status": "Webhook processed"}), 200
|
# don't wait for the process to finish
|
||||||
|
asyncio.create_task(process_wh(request_data ))
|
||||||
|
return jsonify({"status": "OK"}), 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error processing webhook: {e}")
|
logging.error(f"Error processing webhook: {e}")
|
||||||
return jsonify({"error": "Failed to process webhook"}), 500
|
return jsonify({"error": "Failed to process webhook"}), 500
|
||||||
@ -123,14 +133,40 @@ def init_app(tr_handler=None):
|
|||||||
if not token_inputs or not token_outputs:
|
if not token_inputs or not token_outputs:
|
||||||
logging.warning("Missing token inputs or outputs")
|
logging.warning("Missing token inputs or outputs")
|
||||||
return
|
return
|
||||||
|
|
||||||
tr = {
|
|
||||||
'token_in': token_inputs[0]['mint'],
|
|
||||||
'token_out': token_outputs[0]['mint'],
|
|
||||||
'amount_in': float(token_inputs[0]['rawTokenAmount']['tokenAmount']) / 10**token_inputs[0]['rawTokenAmount']['decimals'],
|
|
||||||
'amount_out': float(token_outputs[0]['rawTokenAmount']['tokenAmount']) / 10**token_outputs[0]['rawTokenAmount']['decimals'],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
usdcMint = LIQUIDITY_TOKENS[0]
|
||||||
|
tr = {}
|
||||||
|
if not token_inputs or len(token_inputs) == 0:
|
||||||
|
logging.info("Assumed USDC as first token. BUY transaction detected")
|
||||||
|
tr["token_in"] = usdcMint
|
||||||
|
tr["type"] = "BUY"
|
||||||
|
tr["amount_in"] = await SolanaAPI.DEX.get_token_prices(
|
||||||
|
token_outputs[0]["mint"],
|
||||||
|
int(token_outputs[0]["rawTokenAmount"]["tokenAmount"])
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
token_in = token_inputs[0]
|
||||||
|
tr["token_in"] = token_in["mint"]
|
||||||
|
tr["token_in_decimals"] = token_in["rawTokenAmount"]["decimals"]
|
||||||
|
tr["amount_in"] = float(int(token_in["rawTokenAmount"]["tokenAmount"]) / 10**token_in["rawTokenAmount"]["decimals"])
|
||||||
|
# 'amount_in': float(token_inputs[0]['rawTokenAmount']['tokenAmount']) / 10**token_inputs[0]['rawTokenAmount']['decimals'],
|
||||||
|
|
||||||
|
if not token_outputs or len(token_outputs) == 0:
|
||||||
|
logging.info("Assumed USDC as second token. SELL transaction detected")
|
||||||
|
tr["token_out"] = usdcMint
|
||||||
|
tr["type"] = "SELL"
|
||||||
|
tr["amount_out"] = await SolanaAPI.DEX.get_token_prices(
|
||||||
|
token_inputs[0]["mint"],
|
||||||
|
int(token_inputs[0]["rawTokenAmount"]["tokenAmount"])
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
token_out = token_outputs[0]
|
||||||
|
tr["token_out"] = token_out["mint"]
|
||||||
|
tr["token_out_decimals"] = token_out["rawTokenAmount"]["decimals"]
|
||||||
|
tr["amount_out"] = float(int(token_out["rawTokenAmount"]["tokenAmount"]) / 10**token_out["rawTokenAmount"]["decimals"])
|
||||||
|
#'amount_out': float(token_outputs[0]['rawTokenAmount']['tokenAmount']) / 10**token_outputs[0]['rawTokenAmount']['decimals'],
|
||||||
|
|
||||||
|
|
||||||
if not tr["token_in"] or not tr["token_out"] or tr["amount_in"] == 0 or tr["amount_out"] == 0:
|
if not tr["token_in"] or not tr["token_out"] or tr["amount_in"] == 0 or tr["amount_out"] == 0:
|
||||||
logging.warning("Incomplete swap details found in logs. Getting details from transaction")
|
logging.warning("Incomplete swap details found in logs. Getting details from transaction")
|
||||||
tx_signature = data[0].get('signature')
|
tx_signature = data[0].get('signature')
|
||||||
@ -150,7 +186,7 @@ def init_app(tr_handler=None):
|
|||||||
tr['value_out_USD'] = prices.get(tr['token_out'], 0) * tr['amount_out']
|
tr['value_out_USD'] = prices.get(tr['token_out'], 0) * tr['amount_out']
|
||||||
|
|
||||||
notification = (
|
notification = (
|
||||||
f"<b>Got WH notification:</b>: {tr['amount_in']} {tr['symbol_in']} swapped for {tr['symbol_out']} \n"
|
f"<b>Got WH notification:</b>: {tr['amount_in']} {tr['symbol_in']} swapped for {tr['amount_out']} {tr['symbol_out']} ${tr['value_out_USD']}\n"
|
||||||
)
|
)
|
||||||
logging.info(notification)
|
logging.info(notification)
|
||||||
await utils.telegram_utils.send_telegram_message(notification)
|
await utils.telegram_utils.send_telegram_message(notification)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user