diff --git a/mango/instructiontype.py b/mango/instructiontype.py index 1aa6cd9..c599906 100644 --- a/mango/instructiontype.py +++ b/mango/instructiontype.py @@ -48,6 +48,19 @@ class InstructionType(enum.IntEnum): UpdateRootBank = 21 SettlePnl = 22 SettleBorrow = 23 + ForceCancelSpotOrders = 24 + ForceCancelPerpOrders = 25 + LiquidateTokenAndToken = 26 + LiquidateTokenAndPerp = 27 + LiquidatePerpMarket = 28 + SettleFees = 29 + ResolvePerpBankruptcy = 30 + ResolveTokenBankruptcy = 31 + InitSpotOpenOrders = 32 + RedeemMngo = 33 + AddMangoAccountInfo = 34 + DepositMsrm = 35 + WithdrawMsrm = 36 def __str__(self) -> str: return self.name diff --git a/mango/layouts/layouts.py b/mango/layouts/layouts.py index 4208f57..6f8442e 100644 --- a/mango/layouts/layouts.py +++ b/mango/layouts/layouts.py @@ -905,28 +905,33 @@ ORDERBOOK_SIDE = construct.Struct( # # Here's the [Rust structure](https://github.com/blockworks-foundation/mango-v3/blob/main/program/src/queue.rs): # ``` -# const EVENT_SIZE: usize = 152; -# #[derive(Copy, Clone, Debug, Pod)] -# #[repr(C)] +# const EVENT_SIZE: usize = 200; +# [derive(Copy, Clone, Debug, Pod)] +# [repr(C)] # pub struct FillEvent { # pub event_type: u8, -# pub side: Side, // side from the taker's POV +# pub taker_side: Side, // side from the taker's POV # pub maker_slot: u8, # pub maker_out: bool, // true if maker order quantity == 0 # pub padding: [u8; 4], +# pub timestamp: u64, +# pub seq_num: usize, // note: usize same as u64 +# # pub maker: Pubkey, # pub maker_order_id: i128, # pub maker_client_order_id: u64, +# pub maker_fee: I80F48, # # // The best bid/ask at the time the maker order was placed. Used for liquidity incentives # pub best_initial: i64, # # // Timestamp of when the maker order was placed; copied over from the LeafNode -# pub timestamp: u64, +# pub maker_timestamp: u64, # # pub taker: Pubkey, # pub taker_order_id: i128, # pub taker_client_order_id: u64, +# pub taker_fee: I80F48, # # pub price: i64, # pub quantity: i64, // number of quote lots @@ -934,27 +939,31 @@ ORDERBOOK_SIDE = construct.Struct( # ``` FILL_EVENT = construct.Struct( "event_type" / construct.Const(b'\x00'), - "side" / DecimalAdapter(1), + "taker_side" / DecimalAdapter(1), "maker_slot" / DecimalAdapter(1), "maker_out" / construct.Flag, construct.Padding(4), + "timestamp" / DatetimeAdapter(), + "seq_num" / DecimalAdapter(), + "maker" / PublicKeyAdapter(), "maker_order_id" / SignedDecimalAdapter(16), "maker_client_order_id" / DecimalAdapter(), + "maker_fee" / FloatI80F48Adapter(), "best_initial" / SignedDecimalAdapter(), - - "timestamp" / DatetimeAdapter(), + "maker_timestamp" / DatetimeAdapter(), "taker" / PublicKeyAdapter(), "taker_order_id" / SignedDecimalAdapter(16), "taker_client_order_id" / DecimalAdapter(), + "taker_fee" / FloatI80F48Adapter(), "price" / SignedDecimalAdapter(), "quantity" / SignedDecimalAdapter() ) -_EVENT_SIZE = 152 -assert FILL_EVENT.sizeof() == _EVENT_SIZE +_EVENT_SIZE = 200 +assert FILL_EVENT.sizeof() == _EVENT_SIZE, f"Fill event size is {FILL_EVENT.sizeof()} when it should be {_EVENT_SIZE}." # # πŸ₯­ OUT_EVENT # @@ -967,9 +976,11 @@ assert FILL_EVENT.sizeof() == _EVENT_SIZE # pub side: Side, # pub slot: u8, # padding0: [u8; 5], +# pub timestamp: u64, +# pub seq_num: usize, # pub owner: Pubkey, # pub quantity: i64, -# padding1: [u8; EVENT_SIZE - 48], +# padding1: [u8; EVENT_SIZE - 64], # } # ``` OUT_EVENT = construct.Struct( @@ -977,11 +988,48 @@ OUT_EVENT = construct.Struct( "side" / DecimalAdapter(1), "slot" / DecimalAdapter(1), construct.Padding(5), + "timestamp" / DatetimeAdapter(), + "seq_num" / DecimalAdapter(), "owner" / PublicKeyAdapter(), "quantity" / SignedDecimalAdapter(), - construct.Padding(_EVENT_SIZE - 48) + construct.Padding(_EVENT_SIZE - 64) ) -assert OUT_EVENT.sizeof() == _EVENT_SIZE +assert OUT_EVENT.sizeof() == _EVENT_SIZE, f"Out event size is {OUT_EVENT.sizeof()} when it should be {_EVENT_SIZE}." + +# # πŸ₯­ LIQUIDATE_EVENT +# +# Here's the [Rust structure](https://github.com/blockworks-foundation/mango-v3/blob/main/program/src/queue.rs): +# ``` +# #[derive(Copy, Clone, Debug, Pod)] +# #[repr(C)] +# /// Liquidation for the PerpMarket this EventQueue is for +# pub struct LiquidateEvent { +# pub event_type: u8, +# padding0: [u8; 7], +# pub timestamp: u64, +# pub seq_num: usize, +# pub liqee: Pubkey, +# pub liqor: Pubkey, +# pub price: I80F48, // oracle price at the time of liquidation +# pub quantity: i64, // number of contracts that were moved from liqee to liqor +# pub liquidation_fee: I80F48, // liq fee for this earned for this market +# padding1: [u8; EVENT_SIZE - 128], +# } +# ``` +LIQUIDATE_EVENT = construct.Struct( + "event_type" / construct.Const(b'\x02'), + construct.Padding(7), + "timestamp" / DatetimeAdapter(), + "seq_num" / DecimalAdapter(), + "liquidatee" / PublicKeyAdapter(), + "liquidator" / PublicKeyAdapter(), + "price" / FloatI80F48Adapter(), + "quantity" / SignedDecimalAdapter(), + "liquidation_fee" / FloatI80F48Adapter(), + construct.Padding(_EVENT_SIZE - 128) +) +assert LIQUIDATE_EVENT.sizeof( +) == _EVENT_SIZE, f"Liquidate event size is {LIQUIDATE_EVENT.sizeof()} when it should be {_EVENT_SIZE}." UNKNOWN_EVENT = construct.Struct( "event_type" / construct.Bytes(1), @@ -989,7 +1037,8 @@ UNKNOWN_EVENT = construct.Struct( "owner" / PublicKeyAdapter(), construct.Padding(_EVENT_SIZE - 40) ) -assert UNKNOWN_EVENT.sizeof() == _EVENT_SIZE +assert UNKNOWN_EVENT.sizeof( +) == _EVENT_SIZE, f"Unknown event size is {UNKNOWN_EVENT.sizeof()} when it should be {_EVENT_SIZE}." # # πŸ₯­ PERP_EVENT_QUEUE # @@ -1017,9 +1066,9 @@ PERP_EVENT_QUEUE = construct.Struct( "head" / DecimalAdapter(), "count" / DecimalAdapter(), "seq_num" / DecimalAdapter(), - "maker_fee" / FloatI80F48Adapter(), - "taker_fee" / FloatI80F48Adapter(), - "events" / construct.GreedyRange(construct.Select(FILL_EVENT, OUT_EVENT, UNKNOWN_EVENT)) + # "maker_fee" / FloatI80F48Adapter(), + # "taker_fee" / FloatI80F48Adapter(), + "events" / construct.GreedyRange(construct.Select(FILL_EVENT, OUT_EVENT, LIQUIDATE_EVENT, UNKNOWN_EVENT)) ) # # πŸ₯­ SERUM_EVENT_QUEUE diff --git a/mango/perpeventqueue.py b/mango/perpeventqueue.py index 7dba1b0..5e2588a 100644 --- a/mango/perpeventqueue.py +++ b/mango/perpeventqueue.py @@ -111,6 +111,31 @@ class PerpOutEvent(PerpEvent): return f"""Β« π™ΏπšŽπš›πš™π™Ύπšžπšπ™΄πšŸπšŽπš—πš [{self.original_index}] [{self.owner}] {self.side} {self.quantity}, slot: {self.slot} Β»""" +# # πŸ₯­ PerpLiquidateEvent class +# +# `PerpLiquidateEvent` stores details of a perp 'liquidate' event. +# +class PerpLiquidateEvent(PerpEvent): + def __init__(self, event_type: int, original_index: Decimal, timestamp: datetime, seq_num: Decimal, + liquidatee: PublicKey, liquidator: PublicKey, price: Decimal, quantity: Decimal, + liquidation_fee: Decimal): + super().__init__(event_type, original_index) + self.timestamp: datetime = timestamp + self.seq_num: Decimal = seq_num + self.liquidatee: PublicKey = liquidatee + self.liquidator: PublicKey = liquidator + self.price: Decimal = price + self.quantity: Decimal = quantity + self.liquidation_fee: Decimal = liquidation_fee + + @property + def accounts_to_crank(self) -> typing.Sequence[PublicKey]: + return [self.liquidatee, self.liquidator] + + def __str__(self) -> str: + return f"""Β« π™ΏπšŽπš›πš™π™»πš’πššπšžπš’πšπšŠπšπšŽπ™΄πšŸπšŽπš—πš [{self.original_index}] [{self.owner}] {self.side} {self.quantity}, slot: {self.slot} Β»""" + + # # πŸ₯­ PerpUnknownEvent class # # `PerpUnknownEvent` details an unknown `PerpEvent`. This should never be encountered, but might if @@ -137,7 +162,7 @@ def event_builder(lot_size_converter: LotSizeConverter, event_layout, original_i if event_layout.event_type == b'\x00': if event_layout.maker is None and event_layout.taker is None: return None - side: Side = Side.from_value(event_layout.side) + side: Side = Side.from_value(event_layout.taker_side) quantity: Decimal = lot_size_converter.quantity_lots_to_value(event_layout.quantity) price: Decimal = lot_size_converter.price_lots_to_value(event_layout.price) return PerpFillEvent(event_layout.event_type, original_index, event_layout.timestamp, side, @@ -148,6 +173,8 @@ def event_builder(lot_size_converter: LotSizeConverter, event_layout, original_i event_layout.taker_client_order_id) elif event_layout.event_type == b'\x01': return PerpOutEvent(event_layout.event_type, original_index, event_layout.owner, event_layout.side, event_layout.quantity, event_layout.slot) + elif event_layout.event_type == b'\x02': + return PerpLiquidateEvent(event_layout.event_type, original_index, event_layout.timestamp, event_layout.seq_num, event_layout.liquidatee, event_layout.liquidator, event_layout.price, event_layout.quantity, event_layout.liquidation_fee) else: return PerpUnknownEvent(event_layout.event_type, original_index, event_layout.owner) diff --git a/mango/transactionscout.py b/mango/transactionscout.py index b3805a5..0adca9e 100644 --- a/mango/transactionscout.py +++ b/mango/transactionscout.py @@ -87,7 +87,20 @@ _instruction_signer_indices: typing.Dict[InstructionType, int] = { InstructionType.CancelSpotOrder: 1, InstructionType.UpdateRootBank: -1, # No signer InstructionType.SettlePnl: -1, # No signer - InstructionType.SettleBorrow: -1 # No signer + InstructionType.SettleBorrow: -1, # No signer + InstructionType.ForceCancelSpotOrders: -1, + InstructionType.ForceCancelPerpOrders: -1, + InstructionType.LiquidateTokenAndToken: -1, + InstructionType.LiquidateTokenAndPerp: -1, + InstructionType.LiquidatePerpMarket: -1, + InstructionType.SettleFees: -1, + InstructionType.ResolvePerpBankruptcy: -1, + InstructionType.ResolveTokenBankruptcy: -1, + InstructionType.InitSpotOpenOrders: -1, + InstructionType.RedeemMngo: -1, + InstructionType.AddMangoAccountInfo: -1, + InstructionType.DepositMsrm: -1, + InstructionType.WithdrawMsrm: -1, } # The index of the token IN account depends on the instruction, and for some instructions @@ -117,6 +130,19 @@ _token_in_indices: typing.Dict[InstructionType, int] = { InstructionType.UpdateRootBank: -1, InstructionType.SettlePnl: -1, InstructionType.SettleBorrow: -1, + InstructionType.ForceCancelSpotOrders: -1, + InstructionType.ForceCancelPerpOrders: -1, + InstructionType.LiquidateTokenAndToken: -1, + InstructionType.LiquidateTokenAndPerp: -1, + InstructionType.LiquidatePerpMarket: -1, + InstructionType.SettleFees: -1, + InstructionType.ResolvePerpBankruptcy: -1, + InstructionType.ResolveTokenBankruptcy: -1, + InstructionType.InitSpotOpenOrders: -1, + InstructionType.RedeemMngo: -1, + InstructionType.AddMangoAccountInfo: -1, + InstructionType.DepositMsrm: -1, + InstructionType.WithdrawMsrm: -1, } # The index of the token OUT account depends on the instruction, and for some instructions @@ -146,6 +172,19 @@ _token_out_indices: typing.Dict[InstructionType, int] = { InstructionType.UpdateRootBank: -1, InstructionType.SettlePnl: -1, InstructionType.SettleBorrow: -1, + InstructionType.ForceCancelSpotOrders: -1, + InstructionType.ForceCancelPerpOrders: -1, + InstructionType.LiquidateTokenAndToken: -1, + InstructionType.LiquidateTokenAndPerp: -1, + InstructionType.LiquidatePerpMarket: -1, + InstructionType.SettleFees: -1, + InstructionType.ResolvePerpBankruptcy: -1, + InstructionType.ResolveTokenBankruptcy: -1, + InstructionType.InitSpotOpenOrders: -1, + InstructionType.RedeemMngo: -1, + InstructionType.AddMangoAccountInfo: -1, + InstructionType.DepositMsrm: -1, + InstructionType.WithdrawMsrm: -1, } @@ -175,6 +214,19 @@ _target_indices: typing.Dict[InstructionType, int] = { InstructionType.UpdateRootBank: -1, InstructionType.SettlePnl: -1, InstructionType.SettleBorrow: -1, + InstructionType.ForceCancelSpotOrders: -1, + InstructionType.ForceCancelPerpOrders: -1, + InstructionType.LiquidateTokenAndToken: -1, + InstructionType.LiquidateTokenAndPerp: -1, + InstructionType.LiquidatePerpMarket: -1, + InstructionType.SettleFees: -1, + InstructionType.ResolvePerpBankruptcy: -1, + InstructionType.ResolveTokenBankruptcy: -1, + InstructionType.InitSpotOpenOrders: -1, + InstructionType.RedeemMngo: -1, + InstructionType.AddMangoAccountInfo: -1, + InstructionType.DepositMsrm: -1, + InstructionType.WithdrawMsrm: -1, }