Better logging of failing instructions.
This commit is contained in:
parent
cc6f62c312
commit
7033a080bf
|
@ -96,7 +96,9 @@ class Account(AddressableAccount):
|
||||||
def __init__(self, account_info: AccountInfo, version: Version,
|
def __init__(self, account_info: AccountInfo, version: Version,
|
||||||
meta_data: Metadata, group: Group, owner: PublicKey,
|
meta_data: Metadata, group: Group, owner: PublicKey,
|
||||||
shared_quote_token: AccountBasketToken,
|
shared_quote_token: AccountBasketToken,
|
||||||
basket_indices: typing.Sequence[bool], basket: typing.Sequence[AccountBasketBaseToken],
|
in_margin_basket: typing.Sequence[bool],
|
||||||
|
basket_indices: typing.Sequence[bool],
|
||||||
|
basket: typing.Sequence[AccountBasketBaseToken],
|
||||||
msrm_amount: Decimal, being_liquidated: bool, is_bankrupt: bool):
|
msrm_amount: Decimal, being_liquidated: bool, is_bankrupt: bool):
|
||||||
super().__init__(account_info)
|
super().__init__(account_info)
|
||||||
self.version: Version = version
|
self.version: Version = version
|
||||||
|
@ -105,6 +107,7 @@ class Account(AddressableAccount):
|
||||||
self.group: Group = group
|
self.group: Group = group
|
||||||
self.owner: PublicKey = owner
|
self.owner: PublicKey = owner
|
||||||
self.shared_quote_token: AccountBasketToken = shared_quote_token
|
self.shared_quote_token: AccountBasketToken = shared_quote_token
|
||||||
|
self.in_margin_basket: typing.Sequence[bool] = in_margin_basket
|
||||||
self.basket_indices: typing.Sequence[bool] = basket_indices
|
self.basket_indices: typing.Sequence[bool] = basket_indices
|
||||||
self.basket: typing.Sequence[AccountBasketBaseToken] = basket
|
self.basket: typing.Sequence[AccountBasketBaseToken] = basket
|
||||||
self.msrm_amount: Decimal = msrm_amount
|
self.msrm_amount: Decimal = msrm_amount
|
||||||
|
@ -116,9 +119,10 @@ class Account(AddressableAccount):
|
||||||
meta_data = Metadata.from_layout(layout.meta_data)
|
meta_data = Metadata.from_layout(layout.meta_data)
|
||||||
owner: PublicKey = layout.owner
|
owner: PublicKey = layout.owner
|
||||||
in_margin_basket: typing.Sequence[bool] = list([bool(in_basket) for in_basket in layout.in_margin_basket])
|
in_margin_basket: typing.Sequence[bool] = list([bool(in_basket) for in_basket in layout.in_margin_basket])
|
||||||
|
active_in_basket: typing.List[bool] = []
|
||||||
basket: typing.List[AccountBasketBaseToken] = []
|
basket: typing.List[AccountBasketBaseToken] = []
|
||||||
for index, token_info in enumerate(group.tokens[:-1]):
|
for index, token_info in enumerate(group.tokens[:-1]):
|
||||||
if token_info and in_margin_basket[index]:
|
if token_info:
|
||||||
intrinsic_deposit = token_info.root_bank.deposit_index * layout.deposits[index]
|
intrinsic_deposit = token_info.root_bank.deposit_index * layout.deposits[index]
|
||||||
deposit = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_deposit))
|
deposit = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_deposit))
|
||||||
intrinsic_borrow = token_info.root_bank.borrow_index * layout.borrows[index]
|
intrinsic_borrow = token_info.root_bank.borrow_index * layout.borrows[index]
|
||||||
|
@ -128,6 +132,9 @@ class Account(AddressableAccount):
|
||||||
basket_item: AccountBasketBaseToken = AccountBasketBaseToken(
|
basket_item: AccountBasketBaseToken = AccountBasketBaseToken(
|
||||||
token_info, deposit, borrow, spot_open_orders, perp_account)
|
token_info, deposit, borrow, spot_open_orders, perp_account)
|
||||||
basket += [basket_item]
|
basket += [basket_item]
|
||||||
|
active_in_basket += [True]
|
||||||
|
else:
|
||||||
|
active_in_basket += [False]
|
||||||
|
|
||||||
quote_token_info: typing.Optional[TokenInfo] = group.tokens[-1]
|
quote_token_info: typing.Optional[TokenInfo] = group.tokens[-1]
|
||||||
if quote_token_info is None:
|
if quote_token_info is None:
|
||||||
|
@ -145,7 +152,7 @@ class Account(AddressableAccount):
|
||||||
being_liquidated: bool = bool(layout.being_liquidated)
|
being_liquidated: bool = bool(layout.being_liquidated)
|
||||||
is_bankrupt: bool = bool(layout.is_bankrupt)
|
is_bankrupt: bool = bool(layout.is_bankrupt)
|
||||||
|
|
||||||
return Account(account_info, version, meta_data, group, owner, quote, in_margin_basket, basket, msrm_amount, being_liquidated, is_bankrupt)
|
return Account(account_info, version, meta_data, group, owner, quote, in_margin_basket, active_in_basket, basket, msrm_amount, being_liquidated, is_bankrupt)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(account_info: AccountInfo, group: Group) -> "Account":
|
def parse(account_info: AccountInfo, group: Group) -> "Account":
|
||||||
|
|
|
@ -16,19 +16,42 @@
|
||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
from pyserum._layouts.instructions import InstructionType as SerumInstructionType
|
||||||
from solana.account import Account as SolanaAccount
|
from solana.account import Account as SolanaAccount
|
||||||
from solana.blockhash import Blockhash
|
from solana.blockhash import Blockhash
|
||||||
from solana.publickey import PublicKey
|
from solana.publickey import PublicKey
|
||||||
from solana.transaction import Transaction, TransactionInstruction
|
from solana.transaction import Transaction, TransactionInstruction
|
||||||
|
|
||||||
from .context import Context
|
from .context import Context
|
||||||
|
from .layouts import layouts
|
||||||
|
from .transactionscout import MangoInstruction, InstructionType
|
||||||
from .wallet import Wallet
|
from .wallet import Wallet
|
||||||
|
|
||||||
_MAXIMUM_TRANSACTION_LENGTH = 1280 - 40 - 8
|
_MAXIMUM_TRANSACTION_LENGTH = 1280 - 40 - 8
|
||||||
_SIGNATURE_LENGTH = 64
|
_SIGNATURE_LENGTH = 64
|
||||||
|
|
||||||
|
|
||||||
def _instruction_to_str(instruction: TransactionInstruction) -> str:
|
def _mango_instruction_to_str(instruction: TransactionInstruction) -> str:
|
||||||
|
initial = layouts.MANGO_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
||||||
|
parser = layouts.InstructionParsersByVariant[initial.variant]
|
||||||
|
if parser is None:
|
||||||
|
raise Exception(
|
||||||
|
f"Could not find instruction parser for variant {initial.variant} / {InstructionType(initial.variant)}.")
|
||||||
|
|
||||||
|
accounts: typing.List[PublicKey] = list(map(lambda meta: meta.pubkey, instruction.keys))
|
||||||
|
parsed = parser.parse(instruction.data)
|
||||||
|
instruction_type = InstructionType(int(parsed.variant))
|
||||||
|
|
||||||
|
return str(MangoInstruction(instruction_type, parsed, accounts))
|
||||||
|
|
||||||
|
|
||||||
|
def _serum_instruction_to_str(instruction: TransactionInstruction) -> str:
|
||||||
|
initial = layouts.SERUM_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
||||||
|
instruction_type = SerumInstructionType(initial.variant)
|
||||||
|
return f"« Serum Instruction: {instruction_type.name}: " + "".join("{:02x}".format(x) for x in instruction.data) + "»"
|
||||||
|
|
||||||
|
|
||||||
|
def _raw_instruction_to_str(instruction: TransactionInstruction) -> str:
|
||||||
report: typing.List[str] = []
|
report: typing.List[str] = []
|
||||||
for index, key in enumerate(instruction.keys):
|
for index, key in enumerate(instruction.keys):
|
||||||
report += [f"Key[{index}]: {key.pubkey} {key.is_signer: <5} {key.is_writable: <5}"]
|
report += [f"Key[{index}]: {key.pubkey} {key.is_signer: <5} {key.is_writable: <5}"]
|
||||||
|
@ -37,6 +60,14 @@ def _instruction_to_str(instruction: TransactionInstruction) -> str:
|
||||||
return "\n".join(report)
|
return "\n".join(report)
|
||||||
|
|
||||||
|
|
||||||
|
def _instruction_to_str(context: Context, instruction: TransactionInstruction) -> str:
|
||||||
|
if instruction.program_id == context.program_id:
|
||||||
|
return _mango_instruction_to_str(instruction)
|
||||||
|
elif instruction.program_id == context.dex_program_id:
|
||||||
|
return _serum_instruction_to_str(instruction)
|
||||||
|
return _raw_instruction_to_str(instruction)
|
||||||
|
|
||||||
|
|
||||||
def _split_instructions_into_chunks(signers: typing.Sequence[SolanaAccount], instructions: typing.Sequence[TransactionInstruction]) -> typing.Sequence[typing.Sequence[TransactionInstruction]]:
|
def _split_instructions_into_chunks(signers: typing.Sequence[SolanaAccount], instructions: typing.Sequence[TransactionInstruction]) -> typing.Sequence[typing.Sequence[TransactionInstruction]]:
|
||||||
vetted_chunks: typing.List[typing.List[TransactionInstruction]] = []
|
vetted_chunks: typing.List[typing.List[TransactionInstruction]] = []
|
||||||
current_chunk: typing.List[TransactionInstruction] = []
|
current_chunk: typing.List[TransactionInstruction] = []
|
||||||
|
@ -151,7 +182,7 @@ class CombinableInstructions():
|
||||||
response = context.client.send_transaction(transaction, *self.signers, opts=context.transaction_options)
|
response = context.client.send_transaction(transaction, *self.signers, opts=context.transaction_options)
|
||||||
results += [context.unwrap_or_raise_exception(response)]
|
results += [context.unwrap_or_raise_exception(response)]
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
instruction_str: str = _instruction_to_str(instruction)
|
instruction_str: str = _instruction_to_str(context, instruction)
|
||||||
self.logger.error(f"""Error executing individual instruction: {exception}
|
self.logger.error(f"""Error executing individual instruction: {exception}
|
||||||
{instruction_str}""")
|
{instruction_str}""")
|
||||||
|
|
||||||
|
@ -175,10 +206,12 @@ class CombinableInstructions():
|
||||||
try:
|
try:
|
||||||
response = context.client.send_transaction(transaction, *self.signers, opts=context.transaction_options)
|
response = context.client.send_transaction(transaction, *self.signers, opts=context.transaction_options)
|
||||||
results += [context.unwrap_or_raise_exception(response)]
|
results += [context.unwrap_or_raise_exception(response)]
|
||||||
except:
|
except Exception as exception:
|
||||||
starts_at = sum(len(ch) for ch in chunks[0:index])
|
starts_at = sum(len(ch) for ch in chunks[0:index])
|
||||||
instruction_text = list(map(_instruction_to_str, chunk))
|
instruction_text = "\n".join(list(map(lambda ins: _instruction_to_str(context, ins), chunk)))
|
||||||
self.logger.error(f"""Error executing chunk {index} (instructions {starts_at} to {starts_at + len(chunk)}) of CombinableInstruction. Failing instruction(s):
|
self.logger.error(f"""Error executing chunk {index} (instructions {starts_at} to {starts_at + len(chunk)}) of CombinableInstruction.
|
||||||
|
Exception: {exception}
|
||||||
|
Failing instruction(s):
|
||||||
{instruction_text}""")
|
{instruction_text}""")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
@ -192,7 +225,7 @@ class CombinableInstructions():
|
||||||
report += [f"Signer[{index}]: {signer.public_key()}"]
|
report += [f"Signer[{index}]: {signer.public_key()}"]
|
||||||
|
|
||||||
for instruction in self.instructions:
|
for instruction in self.instructions:
|
||||||
report += _instruction_to_str(instruction)
|
report += _raw_instruction_to_str(instruction)
|
||||||
|
|
||||||
return "\n".join(report)
|
return "\n".join(report)
|
||||||
|
|
||||||
|
|
|
@ -77,12 +77,8 @@ class InventoryAccountWatcher:
|
||||||
self.account_watcher: Watcher[Account] = account_watcher
|
self.account_watcher: Watcher[Account] = account_watcher
|
||||||
account: Account = account_watcher.latest
|
account: Account = account_watcher.latest
|
||||||
base_value = TokenValue.find_by_symbol(account.net_assets, market.base.symbol)
|
base_value = TokenValue.find_by_symbol(account.net_assets, market.base.symbol)
|
||||||
if base_value is None:
|
|
||||||
raise Exception(f"Could not find net assets in account {account.address} for base token {market.base}.")
|
|
||||||
self.base_index: int = account.net_assets.index(base_value)
|
self.base_index: int = account.net_assets.index(base_value)
|
||||||
quote_value = TokenValue.find_by_symbol(account.net_assets, market.quote.symbol)
|
quote_value = TokenValue.find_by_symbol(account.net_assets, market.quote.symbol)
|
||||||
if quote_value is None:
|
|
||||||
raise Exception(f"Could not find net assets in account {account.address} for quote token {market.quote}.")
|
|
||||||
self.quote_index: int = account.net_assets.index(quote_value)
|
self.quote_index: int = account.net_assets.index(quote_value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1101,6 +1101,12 @@ MANGO_INSTRUCTION_VARIANT_FINDER = construct.Struct(
|
||||||
"variant" / construct.BytesInteger(4, swapped=True)
|
"variant" / construct.BytesInteger(4, swapped=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SERUM_INSTRUCTION_VARIANT_FINDER = construct.Struct(
|
||||||
|
"version" / construct.BytesInteger(1, swapped=True),
|
||||||
|
"variant" / construct.BytesInteger(4, swapped=True)
|
||||||
|
)
|
||||||
|
|
||||||
# /// Place an order on a perp market
|
# /// Place an order on a perp market
|
||||||
# /// Accounts expected by this instruction (6):
|
# /// Accounts expected by this instruction (6):
|
||||||
# /// 0. `[]` mango_group_ai - TODO
|
# /// 0. `[]` mango_group_ai - TODO
|
||||||
|
|
|
@ -41,6 +41,11 @@ class Side(enum.Enum):
|
||||||
BUY = "BUY"
|
BUY = "BUY"
|
||||||
SELL = "SELL"
|
SELL = "SELL"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_value(value: Decimal) -> "Side":
|
||||||
|
converted: pyserum.enums.Side = pyserum.enums.Side(int(value))
|
||||||
|
return Side.BUY if converted == pyserum.enums.Side.BUY else Side.SELL
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
@ -63,6 +68,17 @@ class OrderType(enum.Enum):
|
||||||
IOC = "IOC"
|
IOC = "IOC"
|
||||||
POST_ONLY = "POST_ONLY"
|
POST_ONLY = "POST_ONLY"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_value(value: Decimal) -> "OrderType":
|
||||||
|
converted: pyserum.enums.OrderType = pyserum.enums.OrderType(int(value))
|
||||||
|
if converted == pyserum.enums.OrderType.IOC:
|
||||||
|
return OrderType.IOC
|
||||||
|
elif converted == pyserum.enums.OrderType.POST_ONLY:
|
||||||
|
return OrderType.POST_ONLY
|
||||||
|
elif converted == pyserum.enums.OrderType.LIMIT:
|
||||||
|
return OrderType.LIMIT
|
||||||
|
return OrderType.UNKNOWN
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
@ -99,7 +115,7 @@ class Order(typing.NamedTuple):
|
||||||
def from_serum_order(serum_order: SerumOrder) -> "Order":
|
def from_serum_order(serum_order: SerumOrder) -> "Order":
|
||||||
price = Decimal(serum_order.info.price)
|
price = Decimal(serum_order.info.price)
|
||||||
quantity = Decimal(serum_order.info.size)
|
quantity = Decimal(serum_order.info.size)
|
||||||
side = Side.BUY if serum_order.side == pyserum.enums.Side.BUY else Side.SELL
|
side = Side.from_value(serum_order.side)
|
||||||
order = Order(id=serum_order.order_id, side=side, price=price, quantity=quantity,
|
order = Order(id=serum_order.order_id, side=side, price=price, quantity=quantity,
|
||||||
client_id=serum_order.client_id, owner=serum_order.open_order_address,
|
client_id=serum_order.client_id, owner=serum_order.open_order_address,
|
||||||
order_type=OrderType.UNKNOWN)
|
order_type=OrderType.UNKNOWN)
|
||||||
|
|
|
@ -26,6 +26,7 @@ from solana.publickey import PublicKey
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .instructiontype import InstructionType
|
from .instructiontype import InstructionType
|
||||||
from .layouts import layouts
|
from .layouts import layouts
|
||||||
|
from .orders import OrderType, Side
|
||||||
from .ownedtokenvalue import OwnedTokenValue
|
from .ownedtokenvalue import OwnedTokenValue
|
||||||
from .tokenvalue import TokenValue
|
from .tokenvalue import TokenValue
|
||||||
|
|
||||||
|
@ -182,8 +183,6 @@ _target_indices: typing.Dict[InstructionType, int] = {
|
||||||
# This class packages up Mango instruction data, which can come from disparate parts of the
|
# This class packages up Mango instruction data, which can come from disparate parts of the
|
||||||
# transaction. Keeping it all together here makes many things simpler.
|
# transaction. Keeping it all together here makes many things simpler.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class MangoInstruction:
|
class MangoInstruction:
|
||||||
def __init__(self, instruction_type: InstructionType, instruction_data: typing.Any, accounts: typing.Sequence[PublicKey]):
|
def __init__(self, instruction_type: InstructionType, instruction_data: typing.Any, accounts: typing.Sequence[PublicKey]):
|
||||||
self.instruction_type = instruction_type
|
self.instruction_type = instruction_type
|
||||||
|
@ -245,17 +244,17 @@ class MangoInstruction:
|
||||||
elif instruction_type == InstructionType.CacheRootBanks:
|
elif instruction_type == InstructionType.CacheRootBanks:
|
||||||
pass
|
pass
|
||||||
elif instruction_type == InstructionType.PlaceSpotOrder:
|
elif instruction_type == InstructionType.PlaceSpotOrder:
|
||||||
additional_data = f"side: {self.instruction_data.side}, order_type: {self.instruction_data.order_type}, limit_price: {self.instruction_data.limit_price}, max_base_quantity: {self.instruction_data.max_base_quantity}, max_quote_quantity: {self.instruction_data.max_quote_quantity}, self_trade_behavior: {self.instruction_data.self_trade_behavior}, client_id: {self.instruction_data.client_id}, limit: {self.instruction_data.limit}"
|
additional_data = f"side: {Side.from_value(self.instruction_data.side)}, order_type: {OrderType.from_value(self.instruction_data.order_type)}, limit_price: {self.instruction_data.limit_price}, max_base_quantity: {self.instruction_data.max_base_quantity}, max_quote_quantity: {self.instruction_data.max_quote_quantity}, self_trade_behavior: {self.instruction_data.self_trade_behavior}, client_id: {self.instruction_data.client_id}, limit: {self.instruction_data.limit}"
|
||||||
elif instruction_type == InstructionType.AddOracle:
|
elif instruction_type == InstructionType.AddOracle:
|
||||||
pass
|
pass
|
||||||
elif instruction_type == InstructionType.AddPerpMarket:
|
elif instruction_type == InstructionType.AddPerpMarket:
|
||||||
pass
|
pass
|
||||||
elif instruction_type == InstructionType.PlacePerpOrder:
|
elif instruction_type == InstructionType.PlacePerpOrder:
|
||||||
additional_data = f"side: {self.instruction_data.side}, order_type: {self.instruction_data.order_type}, price: {self.instruction_data.price}, quantity: {self.instruction_data.quantity}, client_order_id: {self.instruction_data.client_order_id}"
|
additional_data = f"side: {Side.from_value(self.instruction_data.side)}, order_type: {OrderType.from_value(self.instruction_data.order_type)}, price: {self.instruction_data.price}, quantity: {self.instruction_data.quantity}, client_order_id: {self.instruction_data.client_order_id}"
|
||||||
elif instruction_type == InstructionType.CancelPerpOrderByClientId:
|
elif instruction_type == InstructionType.CancelPerpOrderByClientId:
|
||||||
additional_data = f"client ID: {self.instruction_data.client_order_id}"
|
additional_data = f"client ID: {self.instruction_data.client_order_id}"
|
||||||
elif instruction_type == InstructionType.CancelPerpOrder:
|
elif instruction_type == InstructionType.CancelPerpOrder:
|
||||||
additional_data = f"order ID: {self.instruction_data.order_id}, side: {self.instruction_data.side}"
|
additional_data = f"order ID: {self.instruction_data.order_id}, side: {Side.from_value(self.instruction_data.side)}"
|
||||||
elif instruction_type == InstructionType.ConsumeEvents:
|
elif instruction_type == InstructionType.ConsumeEvents:
|
||||||
additional_data = f"limit: {self.instruction_data.limit}"
|
additional_data = f"limit: {self.instruction_data.limit}"
|
||||||
elif instruction_type == InstructionType.CachePerpMarkets:
|
elif instruction_type == InstructionType.CachePerpMarkets:
|
||||||
|
@ -267,7 +266,7 @@ class MangoInstruction:
|
||||||
elif instruction_type == InstructionType.SettleFunds:
|
elif instruction_type == InstructionType.SettleFunds:
|
||||||
pass
|
pass
|
||||||
elif instruction_type == InstructionType.CancelSpotOrder:
|
elif instruction_type == InstructionType.CancelSpotOrder:
|
||||||
additional_data = f"order ID: {self.instruction_data.order_id}, side: {self.instruction_data.side}"
|
additional_data = f"order ID: {self.instruction_data.order_id}, side: {Side.from_value(self.instruction_data.side)}"
|
||||||
elif instruction_type == InstructionType.UpdateRootBank:
|
elif instruction_type == InstructionType.UpdateRootBank:
|
||||||
pass
|
pass
|
||||||
elif instruction_type == InstructionType.SettlePnl:
|
elif instruction_type == InstructionType.SettlePnl:
|
||||||
|
@ -310,6 +309,10 @@ class MangoInstruction:
|
||||||
|
|
||||||
return MangoInstruction(instruction_type, parsed, accounts)
|
return MangoInstruction(instruction_type, parsed, accounts)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _order_type_enum(side: Decimal):
|
||||||
|
return Side.BUY if side == Decimal(0) else Side.SELL
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
parameters = self.describe_parameters() or "None"
|
parameters = self.describe_parameters() or "None"
|
||||||
return f"« {self.instruction_type.name}: {parameters} »"
|
return f"« {self.instruction_type.name}: {parameters} »"
|
||||||
|
|
|
@ -10,7 +10,8 @@ def test_construction():
|
||||||
meta_data = mango.Metadata(layouts.DATA_TYPE.Group, mango.Version.V1, True)
|
meta_data = mango.Metadata(layouts.DATA_TYPE.Group, mango.Version.V1, True)
|
||||||
group = fake_seeded_public_key("group")
|
group = fake_seeded_public_key("group")
|
||||||
owner = fake_seeded_public_key("owner")
|
owner = fake_seeded_public_key("owner")
|
||||||
in_margin_basket = [False, True, False, True, True]
|
in_margin_basket = [False, False, False, False, False]
|
||||||
|
active_in_basket = [False, True, False, True, True]
|
||||||
quote_deposit = fake_token_value(Decimal(50))
|
quote_deposit = fake_token_value(Decimal(50))
|
||||||
quote_borrow = fake_token_value(Decimal(5))
|
quote_borrow = fake_token_value(Decimal(5))
|
||||||
quote = mango.AccountBasketToken(fake_token_info(), quote_deposit, quote_borrow)
|
quote = mango.AccountBasketToken(fake_token_info(), quote_deposit, quote_borrow)
|
||||||
|
@ -33,7 +34,8 @@ def test_construction():
|
||||||
is_bankrupt = False
|
is_bankrupt = False
|
||||||
|
|
||||||
actual = mango.Account(account_info, mango.Version.V1, meta_data, group, owner, quote,
|
actual = mango.Account(account_info, mango.Version.V1, meta_data, group, owner, quote,
|
||||||
in_margin_basket, basket, msrm_amount, being_liquidated, is_bankrupt)
|
in_margin_basket, active_in_basket, basket, msrm_amount, being_liquidated,
|
||||||
|
is_bankrupt)
|
||||||
|
|
||||||
assert actual is not None
|
assert actual is not None
|
||||||
assert actual.logger is not None
|
assert actual.logger is not None
|
||||||
|
@ -41,7 +43,8 @@ def test_construction():
|
||||||
assert actual.meta_data == meta_data
|
assert actual.meta_data == meta_data
|
||||||
assert actual.group == group
|
assert actual.group == group
|
||||||
assert actual.owner == owner
|
assert actual.owner == owner
|
||||||
assert actual.basket_indices == in_margin_basket
|
assert actual.basket_indices == active_in_basket
|
||||||
|
assert actual.in_margin_basket == in_margin_basket
|
||||||
assert actual.deposits == [None, deposit1, None, deposit2, deposit3, quote_deposit]
|
assert actual.deposits == [None, deposit1, None, deposit2, deposit3, quote_deposit]
|
||||||
assert actual.borrows == [None, borrow1, None, borrow2, borrow3, quote_borrow]
|
assert actual.borrows == [None, borrow1, None, borrow2, borrow3, quote_borrow]
|
||||||
assert actual.net_assets == [None, deposit1 - borrow1, None,
|
assert actual.net_assets == [None, deposit1 - borrow1, None,
|
||||||
|
|
Loading…
Reference in New Issue