mango-explorer/bin/liquidate-single-account

125 lines
5.4 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env python3
import os, sys
from pathlib import Path
# Get the full path to this script.
script_path = Path(os.path.realpath(__file__))
# The parent of the script is the bin directory.
# The parent of the bin directory is the notebook directory.
# It's this notebook directory we want.
notebook_directory = script_path.parent.parent
# Add the notebook directory to our import path.
sys.path.append(str(notebook_directory))
# Add the startup directory to our import path.
startup_directory = notebook_directory / "meta" / "startup"
sys.path.append(str(startup_directory))
import argparse
import logging
import os.path
import projectsetup
import time
import traceback
from solana.publickey import PublicKey
from AccountScout import AccountScout
from AccountLiquidator import ForceCancelOrdersAccountLiquidator
from BaseModel import Group, MarginAccount
from Constants import WARNING_DISCLAIMER_TEXT
from Context import Context, default_cluster, default_cluster_url, default_program_id, default_dex_program_id, default_group_name, default_group_id
from SimpleLiquidator import SimpleLiquidator
from Wallet import Wallet
# We explicitly want argument parsing to be outside the main try-except block because some arguments
# (like --help) will cause an exit, which our except: block traps.
parser = argparse.ArgumentParser(description='Liquidate a single margin account.')
parser.add_argument("--cluster", type=str, default=default_cluster,
help="Solana RPC cluster name")
parser.add_argument("--cluster-url", type=str, default=default_cluster_url,
help="Solana RPC cluster URL")
parser.add_argument("--program-id", type=str, default=default_program_id,
help="Mango program ID/address")
parser.add_argument("--dex-program-id", type=str, default=default_dex_program_id,
help="DEX program ID/address")
parser.add_argument("--group-name", type=str, default=default_group_name,
help="Mango group name")
parser.add_argument("--group-id", type=str, default=default_group_id,
help="Mango group ID/address")
parser.add_argument("--id-file", type=str, default="id.json",
help="file containing the JSON-formatted wallet private key")
parser.add_argument("--log-level", default=logging.WARNING, type=lambda level: getattr(logging, level),
help="level of verbosity to log (possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL)")
parser.add_argument('--margin-account-address', type=PublicKey,
help="Solana address of the Mango Markets margin account to be liquidated")
args = parser.parse_args()
logging.basicConfig(level=args.log_level)
logging.warning(WARNING_DISCLAIMER_TEXT)
try:
id_filename = args.id_file
if not os.path.isfile(id_filename):
logging.error(f"Wallet file '{id_filename}' is not present.")
sys.exit(1)
wallet = Wallet.load(id_filename)
margin_account_address = args.margin_account_address
context = Context(args.cluster, args.cluster_url, args.program_id, args.dex_program_id, args.group_name,
args.group_id)
logging.info(f"Context: {context}")
logging.info(f"Wallet address: {wallet.address}")
logging.info(f"Margin account address: {margin_account_address}")
group = Group.load(context)
logging.info("Checking wallet accounts.")
scout = AccountScout()
report = scout.verify_account_prepared_for_group(context, group, wallet.address)
logging.info(f"Wallet account report: {report}")
if report.has_errors:
raise Exception(f"Account '{wallet.address}' is not prepared for group '{group.address}'.")
logging.info("Wallet accounts OK.")
print("Wallet Balances Before:")
print(f" SOL balance: {context.fetch_sol_balance(wallet.address):>18,.8f}")
for token in group.tokens:
print(f"{token.name:>7} balance: {context.fetch_token_balance(wallet.address, token.mint):>18,.8f}")
prices = group.get_prices()
margin_account = MarginAccount.load(context, margin_account_address, group)
intrinsic_balance_sheets_before = margin_account.get_intrinsic_balance_sheets(group)
print("Margin Account Before:", intrinsic_balance_sheets_before)
liquidator = ForceCancelOrdersAccountLiquidator(context, wallet, group)
transaction_id = liquidator.liquidate(margin_account, prices)
if transaction_id is None:
print("No transaction sent.")
else:
print("Transaction ID:", transaction_id)
print("Waiting for confirmation...")
context.wait_for_confirmation(transaction_id)
group_after = Group.load(context)
margin_account_after_liquidation = MarginAccount.load(context, group_after, margin_account.address)
intrinsic_balance_sheets_after = margin_account_after_liquidation.get_intrinsic_balance_sheets(group_after)
print("Margin Account After:", intrinsic_balance_sheets_after)
print("Wallet Balances After:")
print(f" SOL balance: {context.fetch_sol_balance(wallet.address):>18,.8f}")
for token in group.tokens:
print(f"{token.name:>7} balance: {context.fetch_token_balance(wallet.address, token.mint):>18,.8f}")
except Exception as exception:
logging.critical(f"Liquidator stopped because of exception: {exception} - {traceback.format_exc()}")
except:
logging.critical(f"Liquidator stopped because of uncatchable error: {traceback.format_exc()}")
finally:
logging.info("Liquidation complete.")