Added order placement API. (#31)

Added order placement API including finding the open order account logic.
This commit is contained in:
Leonard G 2020-09-16 23:04:28 +08:00 committed by GitHub
parent 969c14f3e1
commit e2351f5134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 262 additions and 15 deletions

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import logging import logging
import math import math
from typing import Any, Iterable, List, NamedTuple, Tuple from typing import Any, Iterable, List, NamedTuple
from solana.account import Account from solana.account import Account
from solana.publickey import PublicKey from solana.publickey import PublicKey
@ -12,11 +12,14 @@ from solana.transaction import Transaction, TransactionInstruction
from ._layouts.account_flags import ACCOUNT_FLAGS_LAYOUT from ._layouts.account_flags import ACCOUNT_FLAGS_LAYOUT
from ._layouts.market import MARKET_LAYOUT, MINT_LAYOUT from ._layouts.market import MARKET_LAYOUT, MINT_LAYOUT
from ._layouts.open_orders import OPEN_ORDERS_LAYOUT
from ._layouts.slab import Slab from ._layouts.slab import Slab
from .enums import Side from .enums import OrderType, Side
from .instructions import DEFAULT_DEX_PROGRAM_ID, CancelOrderParams, MatchOrdersParams, NewOrderParams from .instructions import DEFAULT_DEX_PROGRAM_ID, CancelOrderParams, MatchOrdersParams, NewOrderParams
from .instructions import cancel_order as cancel_order_inst from .instructions import cancel_order as cancel_order_inst
from .instructions import match_orders as match_order_inst from .instructions import match_orders as match_order_inst
from .instructions import new_order as new_order_inst
from .open_order_account import OpenOrderAccount, make_create_account_instruction
from .queue_ import decode_event_queue, decode_request_queue from .queue_ import decode_event_queue, decode_request_queue
from .utils import load_bytes_data from .utils import load_bytes_data
@ -91,6 +94,22 @@ class Market:
"""Returns quote mint address.""" """Returns quote mint address."""
return PublicKey(self._decode.quote_mint) return PublicKey(self._decode.quote_mint)
def base_vault_address(self) -> PublicKey:
"""Returns base vault address."""
return PublicKey(self._decode.base_vault)
def quote_vault_address(self) -> PublicKey:
"""Returns quote vault address."""
return PublicKey(self._decode.quote_vault)
def request_queue(self) -> PublicKey:
"""Returns quote vault address."""
return PublicKey(self._decode.request_queue)
def event_queue(self) -> PublicKey:
"""Returns quote vault address."""
return PublicKey(self._decode.event_queue)
def __base_spl_token_multiplier(self) -> int: def __base_spl_token_multiplier(self) -> int:
return 10 ** self._base_spl_token_decimals return 10 ** self._base_spl_token_decimals
@ -109,7 +128,12 @@ class Market:
) )
def price_number_to_lots(self, price: float) -> int: def price_number_to_lots(self, price: float) -> int:
raise NotImplementedError("price_number_to_lots is not implemented") return int(
round(
(price * 10 ** self.__quote_spl_token_multiplier() * self._decode.base_lot_size)
/ (10 ** self.__base_spl_token_multiplier() * self._decode.quote_lot_size)
)
)
def base_size_lots_to_number(self, size: int) -> float: def base_size_lots_to_number(self, size: int) -> float:
return float(size * self._decode.base_lot_size) / self.__base_spl_token_multiplier() return float(size * self._decode.base_lot_size) / self.__base_spl_token_multiplier()
@ -183,14 +207,84 @@ class Market:
fee_cost=event.native_fee_or_rebate * (1 if event.event_flags.maker else -1), fee_cost=event.native_fee_or_rebate * (1 if event.event_flags.maker else -1),
) )
def place_order(self, order_params: NewOrderParams): def place_order(
pass self,
payer: PublicKey,
owner: Account,
order_type: OrderType,
side: Side,
limit_price: int,
max_quantity: int,
client_id: int = 0,
):
transaction = Transaction()
signers: List[Account] = [owner]
open_order_accounts = self.find_open_orders_accounts_for_owner(owner.public_key())
if not open_order_accounts:
new_open_order_account = Account()
transaction.add(
make_create_account_instruction(
owner.public_key(),
new_open_order_account.public_key(),
Client(self._endpoint).get_minimum_balance_for_rent_exemption(OPEN_ORDERS_LAYOUT.sizeof())[
"result"
],
self._program_id,
)
)
signers.append(new_open_order_account)
def make_place_order_transaction(self, order_params: NewOrderParams) -> Tuple[Transaction, List[PublicKey]]: transaction.add(
pass self.make_place_order_instruction(
payer,
owner,
order_type,
side,
limit_price,
max_quantity,
client_id,
open_order_accounts[0].address if open_order_accounts else new_open_order_account.public_key(),
)
)
return self._send_transaction(transaction, *signers)
def find_open_orders_accounts_for_owner(self, owner_address: PublicKey): def make_place_order_instruction(
pass self,
payer: PublicKey,
owner: Account,
order_type: OrderType,
side: Side,
limit_price: int,
max_quantity: int,
client_id: int,
open_order_account: PublicKey,
) -> TransactionInstruction:
if self.base_size_number_to_lots(max_quantity) < 0:
raise Exception("Size lot %d is too small." % max_quantity)
if self.price_number_to_lots(limit_price) < 0:
raise Exception("Price lot %d is too small." % limit_price)
return new_order_inst(
NewOrderParams(
market=self.address(),
open_orders=open_order_account,
payer=payer,
owner=owner.public_key(),
request_queue=self.request_queue(),
base_vault=self.base_vault_address(),
quote_vault=self.quote_vault_address(),
side=side,
limit_price=limit_price,
max_quantity=max_quantity,
order_type=order_type,
client_id=client_id,
program_id=self._program_id,
)
)
def find_open_orders_accounts_for_owner(self, owner_address: PublicKey) -> List[OpenOrderAccount]:
return OpenOrderAccount.find_for_market_and_owner(
self._endpoint, self.address(), owner_address, self._program_id
)
def cancel_order_by_client_id(self, owner: str) -> str: def cancel_order_by_client_id(self, owner: str) -> str:
pass pass

View File

@ -1,14 +1,26 @@
from __future__ import annotations from __future__ import annotations
from typing import List import base64
from typing import List, NamedTuple
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.rpc.api import Client from solana.rpc.api import Client, MemcmpOpt
from solana.system_program import CreateAccountParams, create_account
from solana.transaction import TransactionInstruction
from ._layouts.open_orders import OPEN_ORDERS_LAYOUT from ._layouts.open_orders import OPEN_ORDERS_LAYOUT
from .instructions import DEFAULT_DEX_PROGRAM_ID
from .utils import load_bytes_data from .utils import load_bytes_data
class ProgramAccount(NamedTuple):
public_key: PublicKey
data: bytes
is_executablable: bool
lamports: int
owner: PublicKey
class OpenOrderAccount: class OpenOrderAccount:
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
@ -41,6 +53,9 @@ class OpenOrderAccount:
@staticmethod @staticmethod
def from_bytes(address: PublicKey, data_bytes: bytes) -> OpenOrderAccount: def from_bytes(address: PublicKey, data_bytes: bytes) -> OpenOrderAccount:
open_order_decoded = OPEN_ORDERS_LAYOUT.parse(data_bytes) open_order_decoded = OPEN_ORDERS_LAYOUT.parse(data_bytes)
if not open_order_decoded.account_flags.open_orders or not open_order_decoded.account_flags.initialized:
raise Exception("Not an open order account or not initialized.")
return OpenOrderAccount( return OpenOrderAccount(
address=address, address=address,
market=PublicKey(open_order_decoded.market), market=PublicKey(open_order_decoded.market),
@ -56,11 +71,56 @@ class OpenOrderAccount:
) )
@staticmethod @staticmethod
def find_for_market_and_owner(connection: Client, market: PublicKey, owner: PublicKey): def find_for_market_and_owner(
pass endpoint: str, market: PublicKey, owner: PublicKey, program_id: PublicKey
) -> List[OpenOrderAccount]:
filters = [
MemcmpOpt(
offset=5 + 8, # 5 bytes of padding, 8 bytes of account flag
bytes=str(market),
),
MemcmpOpt(
offset=5 + 8 + 32, # 5 bytes of padding, 8 bytes of account flag, 32 bytes of market public key
bytes=str(owner),
),
]
resp = Client(endpoint).get_program_accounts(
program_id, encoding="base64", memcmp_opts=filters, data_size=OPEN_ORDERS_LAYOUT.sizeof()
)
accounts = []
for account in resp["result"]:
account_details = account["account"]
accounts.append(
ProgramAccount(
public_key=PublicKey(account["pubkey"]),
data=base64.decodebytes(account_details["data"][0].encode("ascii")),
is_executablable=bool(account_details["executable"]),
owner=PublicKey(account_details["owner"]),
lamports=int(account_details["lamports"]),
)
)
return [OpenOrderAccount.from_bytes(account.public_key, account.data) for account in accounts]
@staticmethod @staticmethod
def load(endpoint: str, address: str) -> OpenOrderAccount: def load(endpoint: str, address: str) -> OpenOrderAccount:
addr_pub_key = PublicKey(address) addr_pub_key = PublicKey(address)
bytes_data = load_bytes_data(addr_pub_key, endpoint) bytes_data = load_bytes_data(addr_pub_key, endpoint)
return OpenOrderAccount.from_bytes(addr_pub_key, bytes_data) return OpenOrderAccount.from_bytes(addr_pub_key, bytes_data)
def make_create_account_instruction(
owner_address: PublicKey,
new_account_address: PublicKey,
lamports: int,
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID,
) -> TransactionInstruction:
return create_account(
CreateAccountParams(
from_pubkey=owner_address,
new_account_pubkey=new_account_address,
lamports=lamports,
space=OPEN_ORDERS_LAYOUT.sizeof(),
program_id=program_id,
)
)

View File

@ -13,12 +13,12 @@ def _decode_queue(header_layout: Any, node_layout: Any, buffer: bytes, history:
for i in range(min(history, alloc_len)): for i in range(min(history, alloc_len)):
node_index = (header.head + header.count + alloc_len - 1 - i) % alloc_len node_index = (header.head + header.count + alloc_len - 1 - i) % alloc_len
offset = header_layout.sizeof() + node_index * node_layout.sizeof() offset = header_layout.sizeof() + node_index * node_layout.sizeof()
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203 # noqa: E203 nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203
else: else:
for i in range(header.count): for i in range(header.count):
node_index = (header.head + i) % alloc_len node_index = (header.head + i) % alloc_len
offset = header_layout.sizeof() + node_index * node_layout.sizeof() offset = header_layout.sizeof() + node_index * node_layout.sizeof()
nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203 # noqa: E203 nodes.append(node_layout.parse(buffer[offset : offset + node_layout.sizeof()])) # noqa: E203
return header, nodes return header, nodes

View File

@ -0,0 +1 @@
c2VydW0FAAAAAAAAADklQaAz6ADd3zS9MiJs/L7mmunSOSZtNjcUVqTd25yTY3mmdwHMScQ6cpqR7agJJnqKc3TW62zaypJkFqLsl+cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs27bCDgAAAPj///////////////////8HAAAAAAAAAAAAAAAAAAAA///////////0AQAAAAAAAP3/////////COIBAAAAAAD8/////////9IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGFkZGluZw==

View File

@ -1,3 +1,4 @@
_BINARY_DIR_PATH = "tests/binary/" _BINARY_DIR_PATH = "tests/binary/"
ASK_ORDER_BIN_PATH = _BINARY_DIR_PATH + "ask_order_binary.bin" ASK_ORDER_BIN_PATH = _BINARY_DIR_PATH + "ask_order_binary.bin"
EVENT_QUEUE_BIN_PATH = _BINARY_DIR_PATH + "event_queue_binary.bin" EVENT_QUEUE_BIN_PATH = _BINARY_DIR_PATH + "event_queue_binary.bin"
OPEN_ORDER_ACCOUNT_BIN_PATH = _BINARY_DIR_PATH + "open_order_account_binary.bin"

View File

@ -4,6 +4,7 @@ from solana.account import Account
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.rpc.api import Client from solana.rpc.api import Client
from src.enums import OrderType, Side
from src.market import Market from src.market import Market
from .utils import confirm_transaction from .utils import confirm_transaction
@ -77,3 +78,57 @@ def test_match_order(bootstrapped_market: Market, stubbed_payer: Account, http_c
# There should be no ask order. # There should be no ask order.
asks = bootstrapped_market.load_asks() asks = bootstrapped_market.load_asks()
assert sum(1 for _ in asks) == 0 assert sum(1 for _ in asks) == 0
@pytest.mark.integration
def test_new_order(
bootstrapped_market: Market,
stubbed_payer: Account,
http_client: Client,
stubbed_quote_wallet: Account,
stubbed_base_wallet: Account,
):
initial_request_len = len(bootstrapped_market.load_request_queue())
sig = bootstrapped_market.place_order(
payer=stubbed_quote_wallet.public_key(),
owner=stubbed_payer,
side=Side.Buy,
order_type=OrderType.Limit,
limit_price=1000,
max_quantity=3000,
)
confirm_transaction(http_client, sig)
request_queue = bootstrapped_market.load_request_queue()
# 0 request after matching.
assert len(request_queue) == initial_request_len + 1
# There should be no bid order.
bids = bootstrapped_market.load_bids()
assert sum(1 for _ in bids) == 0
# There should be no ask order.
asks = bootstrapped_market.load_asks()
assert sum(1 for _ in asks) == 0
sig = bootstrapped_market.place_order(
payer=stubbed_base_wallet.public_key(),
owner=stubbed_payer,
side=Side.Sell,
order_type=OrderType.Limit,
limit_price=1500,
max_quantity=3000,
)
confirm_transaction(http_client, sig)
# The two order shouldn't get executed since there is a price difference of 1
sig = bootstrapped_market.match_orders(stubbed_payer, 2)
confirm_transaction(http_client, sig)
# There should be 1 bid order that we sent earlier.
bids = bootstrapped_market.load_bids()
assert sum(1 for _ in bids) == 1
# There should be 1 ask order that we sent earlier.
asks = bootstrapped_market.load_asks()
assert sum(1 for _ in asks) == 1

View File

@ -0,0 +1,36 @@
import base64
from solana.publickey import PublicKey
from src.open_order_account import OPEN_ORDERS_LAYOUT, OpenOrderAccount
from .binary_file_path import OPEN_ORDER_ACCOUNT_BIN_PATH
def test_decode_open_order_account_layout():
"""Test decode event queue."""
with open(OPEN_ORDER_ACCOUNT_BIN_PATH, "r") as input_file:
base64_res = input_file.read()
data = base64.decodebytes(base64_res.encode("ascii"))
open_order_account = OPEN_ORDERS_LAYOUT.parse(data)
assert open_order_account.account_flags.open_orders
assert open_order_account.account_flags.initialized
assert PublicKey(open_order_account.market) == PublicKey("4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2")
assert PublicKey(open_order_account.owner) == PublicKey("7hJx7QMiVfjZSSADQ18oNKzqifJPMu18djYLkh4aYh5Q")
# if there is no order the byte returned here will be all 0. In this case we have three orders.
assert len([order for order in open_order_account.orders if int.from_bytes(order, "little") != 0]) == 3
# the first three order are bid order
assert int.from_bytes(open_order_account.is_bid_bits, "little") == 0b111
def test_decode_open_order_account():
"""Test decode event queue."""
with open(OPEN_ORDER_ACCOUNT_BIN_PATH, "r") as input_file:
base64_res = input_file.read()
data = base64.decodebytes(base64_res.encode("ascii"))
open_order_account = OpenOrderAccount.from_bytes(PublicKey(1), data)
assert open_order_account.market == PublicKey("4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2")
assert open_order_account.owner == PublicKey("7hJx7QMiVfjZSSADQ18oNKzqifJPMu18djYLkh4aYh5Q")
assert len([order for order in open_order_account.orders if order != 0]) == 3
# the first three order are bid order
assert open_order_account.is_bid_bits == 0b111