Support Serum v3. (#65)

Added support for DEX v3.
This commit is contained in:
Leonard G 2021-03-01 17:49:04 +08:00 committed by GitHub
parent b5335be638
commit dad9a8e075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 377 additions and 40 deletions

View File

@ -1,7 +1,7 @@
version: '3' version: '3'
services: services:
localnet: localnet:
image: "solanalabs/solana:stable" image: "solanalabs/solana:v1.4.18"
ports: ports:
- "8899:8899" - "8899:8899"
- "8900:8900" - "8900:8900"

View File

@ -16,6 +16,9 @@ class InstructionType(IntEnum):
CancelOrder = 4 CancelOrder = 4
SettleFunds = 5 SettleFunds = 5
CancelOrderByClientID = 6 CancelOrderByClientID = 6
NewOrderV3 = 10
CancelOrderV2 = 11
CancelOrderByClientIdV2 = 12
_VERSION = 0 _VERSION = 0
@ -49,6 +52,24 @@ _CANCEL_ORDER = cStruct(
_CANCEL_ORDER_BY_CLIENTID = cStruct("client_id" / Int64ul) _CANCEL_ORDER_BY_CLIENTID = cStruct("client_id" / Int64ul)
_NEW_ORDER_V3 = cStruct(
"side" / Int32ul, # Enum
"limit_price" / Int64ul,
"max_base_quantity" / Int64ul,
"max_quote_quantity" / Int64ul,
"self_trade_behavior" / Int32ul,
"order_type" / Int32ul, # Enum
"client_id" / Int64ul,
"limit" / Int16ul,
)
_CANCEL_ORDER_V2 = cStruct(
"side" / Int32ul, # Enum
"order_id" / KEY,
)
_CANCEL_ORDER_BY_CLIENTID_V2 = cStruct("client_id" / Int64ul)
INSTRUCTIONS_LAYOUT = cStruct( INSTRUCTIONS_LAYOUT = cStruct(
"version" / Const(_VERSION, Int8ul), "version" / Const(_VERSION, Int8ul),
"instruction_type" / Int32ul, "instruction_type" / Int32ul,
@ -63,6 +84,9 @@ INSTRUCTIONS_LAYOUT = cStruct(
InstructionType.CancelOrder: _CANCEL_ORDER, InstructionType.CancelOrder: _CANCEL_ORDER,
InstructionType.SettleFunds: Pass, # Empty list InstructionType.SettleFunds: Pass, # Empty list
InstructionType.CancelOrderByClientID: _CANCEL_ORDER_BY_CLIENTID, InstructionType.CancelOrderByClientID: _CANCEL_ORDER_BY_CLIENTID,
InstructionType.NewOrderV3: _NEW_ORDER_V3,
InstructionType.CancelOrderV2: _CANCEL_ORDER_V2,
InstructionType.CancelOrderByClientIdV2: _CANCEL_ORDER_BY_CLIENTID_V2,
}, },
), ),
) )

View File

@ -21,3 +21,9 @@ class OrderType(IntEnum):
"""""" """"""
PostOnly = 2 PostOnly = 2
"""""" """"""
class SelfTradeBehavior(IntEnum):
DecrementTake = 0
CancelProvide = 1
AbortTransaction = 2

View File

@ -1,5 +1,5 @@
"""Serum Dex Instructions.""" """Serum Dex Instructions."""
from typing import Any, Dict, List, NamedTuple from typing import Any, Dict, List, NamedTuple, Optional
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.sysvar import SYSVAR_RENT_PUBKEY from solana.sysvar import SYSVAR_RENT_PUBKEY
@ -8,7 +8,7 @@ from solana.utils.validate import validate_instruction_keys, validate_instructio
from spl.token.constants import TOKEN_PROGRAM_ID # type: ignore # TODO: Fix and remove ignore. from spl.token.constants import TOKEN_PROGRAM_ID # type: ignore # TODO: Fix and remove ignore.
from ._layouts.instructions import INSTRUCTIONS_LAYOUT, InstructionType from ._layouts.instructions import INSTRUCTIONS_LAYOUT, InstructionType
from .enums import OrderType, Side from .enums import OrderType, SelfTradeBehavior, Side
# V2 # V2
DEFAULT_DEX_PROGRAM_ID = PublicKey("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o") DEFAULT_DEX_PROGRAM_ID = PublicKey("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o")
@ -177,6 +177,96 @@ class SettleFundsParams(NamedTuple):
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
class NewOrderV3Params(NamedTuple):
"""New order params."""
market: PublicKey
""""""
open_orders: PublicKey
""""""
payer: PublicKey
""""""
owner: PublicKey
""""""
request_queue: PublicKey
""""""
event_queue: PublicKey
""""""
bids: PublicKey
""""""
asks: PublicKey
""""""
base_vault: PublicKey
""""""
quote_vault: PublicKey
""""""
side: Side
""""""
limit_price: int
""""""
max_base_quantity: int
""""""
max_quote_quantity: int
""""""
order_type: OrderType
""""""
self_trade_behavior: SelfTradeBehavior
""""""
limit: Optional[int]
""""""
client_id: int = 0
""""""
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
""""""
fee_discount_pubkey: Optional[PublicKey] = None
class CancelOrderV2Params(NamedTuple):
"""Cancel order params."""
market: PublicKey
""""""
bids: PublicKey
""""""
asks: PublicKey
""""""
event_queue: PublicKey
""""""
open_orders: PublicKey
""""""
owner: PublicKey
""""""
side: Side
""""""
order_id: int
""""""
open_orders_slot: int
""""""
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
""""""
class CancelOrderByClientIDV2Params(NamedTuple):
"""Cancel order by client ID params."""
market: PublicKey
""""""
bids: PublicKey
""""""
asks: PublicKey
""""""
event_queue: PublicKey
""""""
open_orders: PublicKey
""""""
owner: PublicKey
""""""
client_id: int
""""""
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
""""""
def __parse_and_validate_instruction(instruction: TransactionInstruction, instruction_type: InstructionType) -> Any: def __parse_and_validate_instruction(instruction: TransactionInstruction, instruction_type: InstructionType) -> Any:
instruction_type_to_length_map: Dict[InstructionType, int] = { instruction_type_to_length_map: Dict[InstructionType, int] = {
InstructionType.InitializeMarket: 9, InstructionType.InitializeMarket: 9,
@ -186,6 +276,9 @@ def __parse_and_validate_instruction(instruction: TransactionInstruction, instru
InstructionType.CancelOrder: 4, InstructionType.CancelOrder: 4,
InstructionType.CancelOrderByClientID: 4, InstructionType.CancelOrderByClientID: 4,
InstructionType.SettleFunds: 9, InstructionType.SettleFunds: 9,
InstructionType.NewOrderV3: 12,
InstructionType.CancelOrderV2: 6,
InstructionType.CancelOrderByClientIdV2: 6,
} }
validate_instruction_keys(instruction, instruction_type_to_length_map[instruction_type]) validate_instruction_keys(instruction, instruction_type_to_length_map[instruction_type])
data = INSTRUCTIONS_LAYOUT.parse(instruction.data) data = INSTRUCTIONS_LAYOUT.parse(instruction.data)
@ -297,6 +390,58 @@ def decode_cancel_order_by_client_id(instruction: TransactionInstruction) -> Can
) )
def decode_new_order_v3(instruction: TransactionInstruction) -> NewOrderV3Params:
data = __parse_and_validate_instruction(instruction, InstructionType.NewOrderV3)
return NewOrderV3Params(
market=instruction.keys[0].pubkey,
open_orders=instruction.keys[1].pubkey,
request_queue=instruction.keys[2].pubkey,
event_queue=instruction.keys[3].pubkey,
bids=instruction.keys[4].pubkey,
asks=instruction.keys[5].pubkey,
payer=instruction.keys[6].pubkey,
owner=instruction.keys[7].pubkey,
base_vault=instruction.keys[8].pubkey,
quote_vault=instruction.keys[9].pubkey,
side=data.args.side,
limit_price=data.args.limit_price,
max_base_quantity=data.args.max_base_quantity,
max_quote_quantity=data.args.max_quote_quantity,
self_trade_behavior=SelfTradeBehavior(data.args.self_trade_behavior),
order_type=OrderType(data.args.order_type),
client_id=data.args.client_id,
limit=data.args.limit,
)
def decode_cancel_order_v2(instruction: TransactionInstruction) -> CancelOrderV2Params:
data = __parse_and_validate_instruction(instruction, InstructionType.CancelOrderV2)
return CancelOrderV2Params(
market=instruction.keys[0].pubkey,
bids=instruction.keys[1].pubkey,
asks=instruction.keys[2].pubkey,
open_orders=instruction.keys[3].pubkey,
owner=instruction.keys[4].pubkey,
event_queue=instruction.keys[5].pubkey,
side=Side(data.args.side),
order_id=int.from_bytes(data.args.order_id, "little"),
open_orders_slot=data.args.open_orders_slot,
)
def decode_cancel_order_by_client_id_v2(instruction: TransactionInstruction) -> CancelOrderByClientIDV2Params:
data = __parse_and_validate_instruction(instruction, InstructionType.CancelOrderByClientIdV2)
return CancelOrderByClientIDV2Params(
market=instruction.keys[0].pubkey,
bids=instruction.keys[1].pubkey,
asks=instruction.keys[2].pubkey,
open_orders=instruction.keys[3].pubkey,
owner=instruction.keys[4].pubkey,
event_queue=instruction.keys[5].pubkey,
client_id=data.args.client_id,
)
def initialize_market(params: InitializeMarketParams) -> TransactionInstruction: def initialize_market(params: InitializeMarketParams) -> TransactionInstruction:
"""Generate a transaction instruction to initialize a Serum market.""" """Generate a transaction instruction to initialize a Serum market."""
return TransactionInstruction( return TransactionInstruction(
@ -453,3 +598,91 @@ def cancel_order_by_client_id(params: CancelOrderByClientIDParams) -> Transactio
) )
), ),
) )
def new_order_v3(params: NewOrderV3Params) -> TransactionInstruction:
"""Generate a transaction instruction to place new order."""
touched_keys = [
AccountMeta(pubkey=params.market, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.request_queue, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.event_queue, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.bids, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.asks, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.payer, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.owner, is_signer=True, is_writable=False),
AccountMeta(pubkey=params.base_vault, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.quote_vault, is_signer=False, is_writable=True),
AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSVAR_RENT_PUBKEY, is_signer=False, is_writable=False),
]
if params.fee_discount_pubkey:
touched_keys.append(
AccountMeta(pubkey=params.fee_discount_pubkey, is_signer=False, is_writable=False),
)
return TransactionInstruction(
keys=touched_keys,
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.NewOrderV3,
args=dict(
side=params.side,
limit_price=params.limit_price,
max_base_quantity=params.max_base_quantity,
max_quote_quantity=params.max_quote_quantity,
self_trade_behavior=params.self_trade_behavior,
order_type=params.order_type,
client_id=params.client_id,
limit=65535,
),
)
),
)
def cancel_order_v2(params: CancelOrderV2Params) -> TransactionInstruction:
"""Generate a transaction instruction to cancel order."""
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
AccountMeta(pubkey=params.bids, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.asks, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.owner, is_signer=True, is_writable=False),
AccountMeta(pubkey=params.event_queue, is_signer=False, is_writable=True),
],
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.CancelOrderV2,
args=dict(
side=params.side,
order_id=params.order_id.to_bytes(16, byteorder="little"),
),
)
),
)
def cancel_order_by_client_id_v2(params: CancelOrderByClientIDV2Params) -> TransactionInstruction:
"""Generate a transaction instruction to cancel order by client id."""
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
AccountMeta(pubkey=params.bids, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.asks, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.owner, is_signer=True, is_writable=False),
AccountMeta(pubkey=params.event_queue, is_signer=False, is_writable=True),
],
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(
dict(
instruction_type=InstructionType.CancelOrderByClientIdV2,
args=dict(
client_id=params.client_id,
),
)
),
)

View File

@ -20,7 +20,7 @@ import pyserum.instructions as instructions
import pyserum.market.types as t import pyserum.market.types as t
from .._layouts.open_orders import OPEN_ORDERS_LAYOUT from .._layouts.open_orders import OPEN_ORDERS_LAYOUT
from ..enums import OrderType, Side from ..enums import OrderType, SelfTradeBehavior, Side
from ..open_orders_account import OpenOrdersAccount, make_create_account_instruction from ..open_orders_account import OpenOrdersAccount, make_create_account_instruction
from ..utils import load_bytes_data from ..utils import load_bytes_data
from ._internal.queue import decode_event_queue, decode_request_queue from ._internal.queue import decode_event_queue, decode_request_queue
@ -36,13 +36,10 @@ class Market:
logger = logging.getLogger("pyserum.market.Market") logger = logging.getLogger("pyserum.market.Market")
def __init__( def __init__(self, conn: Client, market_state: MarketState, force_use_request_queue: bool = False) -> None:
self,
conn: Client,
market_state: MarketState,
) -> None:
self._conn = conn self._conn = conn
self.state = market_state self.state = market_state
self.force_use_request_queue = force_use_request_queue
@staticmethod @staticmethod
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -50,6 +47,7 @@ class Market:
conn: Client, conn: Client,
market_address: PublicKey, market_address: PublicKey,
program_id: PublicKey = instructions.DEFAULT_DEX_PROGRAM_ID, program_id: PublicKey = instructions.DEFAULT_DEX_PROGRAM_ID,
force_use_request_queue: bool = False,
) -> Market: ) -> Market:
"""Factory method to create a Market. """Factory method to create a Market.
@ -58,7 +56,20 @@ class Market:
:param program_id: The program id of the given market, it will use the default value if not provided. :param program_id: The program id of the given market, it will use the default value if not provided.
""" """
market_state = MarketState.load(conn, market_address, program_id) market_state = MarketState.load(conn, market_address, program_id)
return Market(conn, market_state) return Market(conn, market_state, force_use_request_queue)
def _use_request_queue(self) -> bool:
return (
# DEX Version 1
self.state.program_id == PublicKey("4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn")
or
# DEX Version 1
self.state.program_id == PublicKey("BJ3jrUzddfuSrZHXSCxMUUQsjKEyLmuuyZebkcaFp2fg")
or
# DEX Version 2
self.state.program_id == PublicKey("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o")
or self.force_use_request_queue
)
def support_srm_fee_discounts(self) -> bool: def support_srm_fee_discounts(self) -> bool:
raise NotImplementedError("support_srm_fee_discounts not implemented") raise NotImplementedError("support_srm_fee_discounts not implemented")
@ -158,8 +169,8 @@ class Market:
owner: Account, owner: Account,
order_type: OrderType, order_type: OrderType,
side: Side, side: Side,
limit_price: int, limit_price: float,
max_quantity: int, max_quantity: float,
client_id: int = 0, client_id: int = 0,
opts: TxOpts = TxOpts(), opts: TxOpts = TxOpts(),
) -> RPCResponse: # TODO: Add open_orders_address_key param and fee_discount_pubkey ) -> RPCResponse: # TODO: Add open_orders_address_key param and fee_discount_pubkey
@ -245,7 +256,7 @@ class Market:
@staticmethod @staticmethod
def _get_lamport_need_for_sol_wrapping( def _get_lamport_need_for_sol_wrapping(
price: int, size: int, side: Side, open_orders_accounts: List[OpenOrdersAccount] price: float, size: float, side: Side, open_orders_accounts: List[OpenOrdersAccount]
) -> int: ) -> int:
lamports = 0 lamports = 0
if side == Side.Buy: if side == Side.Buy:
@ -265,30 +276,56 @@ class Market:
owner: Account, owner: Account,
order_type: OrderType, order_type: OrderType,
side: Side, side: Side,
limit_price: int, limit_price: float,
max_quantity: int, max_quantity: float,
client_id: int, client_id: int,
open_order_account: PublicKey, open_order_account: PublicKey,
fee_discount_pubkey: PublicKey = None,
) -> TransactionInstruction: ) -> TransactionInstruction:
if self.state.base_size_number_to_lots(max_quantity) < 0: if self.state.base_size_number_to_lots(max_quantity) < 0:
raise Exception("Size lot %d is too small" % max_quantity) raise Exception("Size lot %d is too small" % max_quantity)
if self.state.price_number_to_lots(limit_price) < 0: if self.state.price_number_to_lots(limit_price) < 0:
raise Exception("Price lot %d is too small" % limit_price) raise Exception("Price lot %d is too small" % limit_price)
return instructions.new_order( if self._use_request_queue():
instructions.NewOrderParams( return instructions.new_order(
instructions.NewOrderParams(
market=self.state.public_key(),
open_orders=open_order_account,
payer=payer,
owner=owner.public_key(),
request_queue=self.state.request_queue(),
base_vault=self.state.base_vault(),
quote_vault=self.state.quote_vault(),
side=side,
limit_price=self.state.price_number_to_lots(limit_price),
max_quantity=self.state.base_size_number_to_lots(max_quantity),
order_type=order_type,
client_id=client_id,
program_id=self.state.program_id(),
)
)
return instructions.new_order_v3(
instructions.NewOrderV3Params(
market=self.state.public_key(), market=self.state.public_key(),
open_orders=open_order_account, open_orders=open_order_account,
payer=payer, payer=payer,
owner=owner.public_key(), owner=owner.public_key(),
request_queue=self.state.request_queue(), request_queue=self.state.request_queue(),
event_queue=self.state.event_queue(),
bids=self.state.bids(),
asks=self.state.asks(),
base_vault=self.state.base_vault(), base_vault=self.state.base_vault(),
quote_vault=self.state.quote_vault(), quote_vault=self.state.quote_vault(),
side=side, side=side,
limit_price=limit_price, limit_price=self.state.price_number_to_lots(limit_price),
max_quantity=max_quantity, max_base_quantity=self.state.base_size_number_to_lots(max_quantity),
max_quote_quantity=self.state.quote_size_number_to_lots(max_quantity * limit_price),
order_type=order_type, order_type=order_type,
client_id=client_id, client_id=client_id,
program_id=self.state.program_id(), program_id=self.state.program_id(),
self_trade_behavior=SelfTradeBehavior.DecrementTake,
fee_discount_pubkey=fee_discount_pubkey,
limit=65535,
) )
) )
@ -301,12 +338,25 @@ class Market:
def make_cancel_order_by_client_id_instruction( def make_cancel_order_by_client_id_instruction(
self, owner: Account, open_orders_account: PublicKey, client_id: int self, owner: Account, open_orders_account: PublicKey, client_id: int
) -> TransactionInstruction: ) -> TransactionInstruction:
return instructions.cancel_order_by_client_id( if self._use_request_queue():
instructions.CancelOrderByClientIDParams( return instructions.cancel_order_by_client_id(
instructions.CancelOrderByClientIDParams(
market=self.state.public_key(),
owner=owner.public_key(),
open_orders=open_orders_account,
request_queue=self.state.request_queue(),
client_id=client_id,
program_id=self.state.program_id(),
)
)
return instructions.cancel_order_by_client_id_v2(
instructions.CancelOrderByClientIDV2Params(
market=self.state.public_key(), market=self.state.public_key(),
owner=owner.public_key(), owner=owner.public_key(),
open_orders=open_orders_account, open_orders=open_orders_account,
request_queue=self.state.request_queue(), bids=self.state.bids(),
asks=self.state.asks(),
event_queue=self.state.event_queue(),
client_id=client_id, client_id=client_id,
program_id=self.state.program_id(), program_id=self.state.program_id(),
) )
@ -316,23 +366,39 @@ class Market:
txn = Transaction().add(self.make_cancel_order_instruction(owner.public_key(), order)) txn = Transaction().add(self.make_cancel_order_instruction(owner.public_key(), order))
return self._conn.send_transaction(txn, owner, opts=opts) return self._conn.send_transaction(txn, owner, opts=opts)
def make_cancel_order_instruction(self, owner: PublicKey, order: t.Order) -> TransactionInstruction:
if self._use_request_queue():
return instructions.cancel_order(
instructions.CancelOrderParams(
market=self.state.public_key(),
owner=owner,
open_orders=order.open_order_address,
request_queue=self.state.request_queue(),
side=order.side,
order_id=order.order_id,
open_orders_slot=order.open_order_slot,
program_id=self.state.program_id(),
)
)
return instructions.cancel_order_v2(
instructions.CancelOrderV2Params(
market=self.state.public_key(),
owner=owner,
open_orders=order.open_order_address,
bids=self.state.bids(),
asks=self.state.asks(),
event_queue=self.state.event_queue(),
side=order.side,
order_id=order.order_id,
open_orders_slot=order.open_order_slot,
program_id=self.state.program_id(),
)
)
def match_orders(self, fee_payer: Account, limit: int, opts: TxOpts = TxOpts()) -> RPCResponse: def match_orders(self, fee_payer: Account, limit: int, opts: TxOpts = TxOpts()) -> RPCResponse:
txn = Transaction().add(self.make_match_orders_instruction(limit)) txn = Transaction().add(self.make_match_orders_instruction(limit))
return self._conn.send_transaction(txn, fee_payer, opts=opts) return self._conn.send_transaction(txn, fee_payer, opts=opts)
def make_cancel_order_instruction(self, owner: PublicKey, order: t.Order) -> TransactionInstruction:
params = instructions.CancelOrderParams(
market=self.state.public_key(),
owner=owner,
open_orders=order.open_order_address,
request_queue=self.state.request_queue(),
side=order.side,
order_id=order.order_id,
open_orders_slot=order.open_order_slot,
program_id=self.state.program_id(),
)
return instructions.cancel_order(params)
def make_match_orders_instruction(self, limit: int) -> TransactionInstruction: def make_match_orders_instruction(self, limit: int) -> TransactionInstruction:
params = instructions.MatchOrdersParams( params = instructions.MatchOrdersParams(
market=self.state.public_key(), market=self.state.public_key(),

View File

@ -147,4 +147,10 @@ class MarketState: # pylint: disable=too-many-public-methods
return float(size * self.base_lot_size()) / self.base_spl_token_multiplier() return float(size * self.base_lot_size()) / self.base_spl_token_multiplier()
def base_size_number_to_lots(self, size: float) -> int: def base_size_number_to_lots(self, size: float) -> int:
return int(math.floor(size * 10 ** self._base_mint_decimals) / self.base_lot_size()) return int(math.floor(size * self.base_spl_token_multiplier()) / self.base_lot_size())
def quote_size_lots_to_number(self, size: int) -> float:
return float(size * self.quote_lot_size()) / self.quote_spl_token_multiplier()
def quote_size_number_to_lots(self, size: float) -> int:
return int(math.floor(size * self.quote_spl_token_multiplier()) / self.quote_lot_size())

View File

@ -13,15 +13,17 @@ else
exit 1 exit 1
fi fi
docker-compose up -d
if ! hash solana 2>/dev/null; then if ! hash solana 2>/dev/null; then
echo Installing Solana tool suite ... echo Installing Solana tool suite ...
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.3.9/install/solana-install-init.sh | sh -s - v1.3.9 curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.5.8/install/solana-install-init.sh | SOLANA_RELEASE=v1.5.8 sh -s - v1.5.8
export PATH="/home/runner/.local/share/solana/install/active_release/bin:$PATH" export PATH="/home/runner/.local/share/solana/install/active_release/bin:$PATH"
echo Generating keypair ... echo Generating keypair ...
solana-keygen new -o ~/.config/solana/id.json --no-passphrase --silent solana-keygen new -o ~/.config/solana/id.json --no-passphrase --silent
fi fi
solana config set --url "http://localhost:8899"
solana-test-validator &
solana config set --url "http://127.0.0.1:8899"
curl -s -L "https://github.com/serum-community/serum-dex/releases/download/v2/serum_dex-$os_type.so" > serum_dex.so curl -s -L "https://github.com/serum-community/serum-dex/releases/download/v2/serum_dex-$os_type.so" > serum_dex.so
sleep 1 sleep 1
solana airdrop 10000 solana airdrop 10000

View File

@ -13,7 +13,7 @@ from pyserum.market import Market
@pytest.mark.integration @pytest.mark.integration
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def bootstrapped_market(http_client: Client, stubbed_market_pk: PublicKey, stubbed_dex_program_pk: PublicKey) -> Market: def bootstrapped_market(http_client: Client, stubbed_market_pk: PublicKey, stubbed_dex_program_pk: PublicKey) -> Market:
return Market.load(http_client, stubbed_market_pk, stubbed_dex_program_pk) return Market.load(http_client, stubbed_market_pk, stubbed_dex_program_pk, force_use_request_queue=True)
@pytest.mark.integration @pytest.mark.integration