Skip to content

Commit

Permalink
Added support for init and close open_orders_accounts instructions (#99)
Browse files Browse the repository at this point in the history
* added support for init and close open_orders_accounts instructions

* removed unused variables
  • Loading branch information
quazzuk authored Dec 23, 2021
1 parent 6f3ba27 commit d0938e9
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 32 deletions.
9 changes: 8 additions & 1 deletion pyserum/_layouts/instructions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Layouts for dex instructions data."""
from enum import IntEnum

from construct import Switch
from construct import Bytes, Const, Int8ul, Int16ul, Int32ul, Int64ul, Pass
from construct import Struct as cStruct
from construct import Switch

from .slab import KEY

Expand All @@ -19,6 +19,8 @@ class InstructionType(IntEnum):
NEW_ORDER_V3 = 10
CANCEL_ORDER_V2 = 11
CANCEL_ORDER_BY_CLIENT_ID_V2 = 12
CLOSE_OPEN_ORDERS = 14
INIT_OPEN_ORDERS = 15


_VERSION = 0
Expand Down Expand Up @@ -70,6 +72,9 @@ class InstructionType(IntEnum):

_CANCEL_ORDER_BY_CLIENTID_V2 = cStruct("client_id" / Int64ul)

_CLOSE_OPEN_ORDERS = cStruct()
_INIT_OPEN_ORDERS = cStruct()

INSTRUCTIONS_LAYOUT = cStruct(
"version" / Const(_VERSION, Int8ul),
"instruction_type" / Int32ul,
Expand All @@ -87,6 +92,8 @@ class InstructionType(IntEnum):
InstructionType.NEW_ORDER_V3: _NEW_ORDER_V3,
InstructionType.CANCEL_ORDER_V2: _CANCEL_ORDER_V2,
InstructionType.CANCEL_ORDER_BY_CLIENT_ID_V2: _CANCEL_ORDER_BY_CLIENTID_V2,
InstructionType.CLOSE_OPEN_ORDERS: _CLOSE_OPEN_ORDERS,
InstructionType.INIT_OPEN_ORDERS: _INIT_OPEN_ORDERS,
},
),
)
3 changes: 2 additions & 1 deletion pyserum/_layouts/open_orders.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from construct import Bytes, Int64ul, Padding, Struct as cStruct
from construct import Bytes, Int64ul, Padding
from construct import Struct as cStruct

from .account_flags import ACCOUNT_FLAGS_LAYOUT

Expand Down
2 changes: 1 addition & 1 deletion pyserum/_layouts/queue.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from construct import BitStruct, BitsInteger, BitsSwapped, Bytes, Const, Flag, Int8ul, Int32ul, Int64ul, Padding
from construct import BitsInteger, BitsSwapped, BitStruct, Bytes, Const, Flag, Int8ul, Int32ul, Int64ul, Padding
from construct import Struct as cStruct

from .account_flags import ACCOUNT_FLAGS_LAYOUT
Expand Down
3 changes: 2 additions & 1 deletion pyserum/_layouts/slab.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

from enum import IntEnum

from construct import Switch, Bytes, Int8ul, Int32ul, Int64ul, Padding
from construct import Bytes, Int8ul, Int32ul, Int64ul, Padding
from construct import Struct as cStruct
from construct import Switch

from .account_flags import ACCOUNT_FLAGS_LAYOUT

Expand Down
3 changes: 2 additions & 1 deletion pyserum/async_connection.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import List

import httpx
from solana.rpc.async_api import AsyncClient as async_conn # pylint: disable=unused-import # noqa:F401

from .market.types import MarketInfo, TokenInfo
from .connection import LIVE_MARKETS_URL, TOKEN_MINTS_URL, parse_live_markets, parse_token_mints
from .market.types import MarketInfo, TokenInfo


async def get_live_markets(httpx_client: httpx.AsyncClient) -> List[MarketInfo]:
Expand Down
5 changes: 3 additions & 2 deletions pyserum/async_open_orders_account.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from typing import List
from solana.rpc.async_api import AsyncClient

from solana.publickey import PublicKey
from solana.rpc.types import Commitment
from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Recent
from solana.rpc.types import Commitment

from .async_utils import load_bytes_data
from .open_orders_account import _OpenOrdersAccountCore
Expand Down
6 changes: 3 additions & 3 deletions pyserum/connection.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import List, Dict, Any
from typing import Any, Dict, List

import requests

from solana.rpc.api import Client as conn # pylint: disable=unused-import # noqa:F401
from solana.publickey import PublicKey
from solana.rpc.api import Client as conn # pylint: disable=unused-import # noqa:F401

from .market.types import MarketInfo, TokenInfo

LIVE_MARKETS_URL = "https://raw.githubusercontent.com/project-serum/serum-ts/master/packages/serum/src/markets.json"
Expand Down
115 changes: 108 additions & 7 deletions pyserum/instructions.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Serum Dex Instructions."""
from typing import Dict, List, NamedTuple, Optional

from construct import Container
from solana.publickey import PublicKey
from solana.sysvar import SYSVAR_RENT_PUBKEY
from solana.transaction import AccountMeta, TransactionInstruction
from solana.utils.validate import validate_instruction_keys, validate_instruction_type
from spl.token.constants import TOKEN_PROGRAM_ID
from construct import Container

from ._layouts.instructions import INSTRUCTIONS_LAYOUT, InstructionType
from .enums import OrderType, SelfTradeBehavior, Side
Expand Down Expand Up @@ -268,6 +268,36 @@ class CancelOrderByClientIDV2Params(NamedTuple):
""""""


class CloseOpenOrdersParams(NamedTuple):
"""Cancel order by client ID params."""

open_orders: PublicKey
""""""
owner: PublicKey
""""""
sol_wallet: PublicKey
""""""
market: PublicKey
""""""
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
""""""


class InitOpenOrdersParams(NamedTuple):
"""Cancel order by client ID params."""

open_orders: PublicKey
""""""
owner: PublicKey
""""""
market: PublicKey
""""""
market_authority: Optional[PublicKey] = None
""""""
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
""""""


def __parse_and_validate_instruction(
instruction: TransactionInstruction, instruction_type: InstructionType
) -> Container:
Expand All @@ -282,14 +312,18 @@ def __parse_and_validate_instruction(
InstructionType.NEW_ORDER_V3: 12,
InstructionType.CANCEL_ORDER_V2: 6,
InstructionType.CANCEL_ORDER_BY_CLIENT_ID_V2: 6,
InstructionType.CLOSE_OPEN_ORDERS: 4,
InstructionType.INIT_OPEN_ORDERS: 3,
}
validate_instruction_keys(instruction, instruction_type_to_length_map[instruction_type])
data = INSTRUCTIONS_LAYOUT.parse(instruction.data)
validate_instruction_type(data, instruction_type)
return data


def decode_initialize_market(instruction: TransactionInstruction) -> InitializeMarketParams:
def decode_initialize_market(
instruction: TransactionInstruction,
) -> InitializeMarketParams:
"""Decode an instialize market instruction and retrieve the instruction params."""
data = __parse_and_validate_instruction(instruction, InstructionType.INITIALIZE_MARKET)
return InitializeMarketParams(
Expand Down Expand Up @@ -382,7 +416,9 @@ def decode_settle_funds(instruction: TransactionInstruction) -> SettleFundsParam
)


def decode_cancel_order_by_client_id(instruction: TransactionInstruction) -> CancelOrderByClientIDParams:
def decode_cancel_order_by_client_id(
instruction: TransactionInstruction,
) -> CancelOrderByClientIDParams:
data = __parse_and_validate_instruction(instruction, InstructionType.CANCEL_ORDER_BY_CLIENT_ID)
return CancelOrderByClientIDParams(
market=instruction.keys[0].pubkey,
Expand Down Expand Up @@ -445,6 +481,29 @@ def decode_cancel_order_by_client_id_v2(instruction: TransactionInstruction) ->
)


def decode_close_open_orders(
instruction: TransactionInstruction,
) -> CloseOpenOrdersParams:
return CloseOpenOrdersParams(
open_orders=instruction.keys[0].pubkey,
owner=instruction.keys[1].pubkey,
sol_wallet=instruction.keys[2].pubkey,
market=instruction.keys[3].pubkey,
)


def decode_init_open_orders(
instruction: TransactionInstruction,
) -> InitOpenOrdersParams:
market_authority = instruction.keys[3].pubkey if len(instruction.keys) == 4 else None
return InitOpenOrdersParams(
open_orders=instruction.keys[0].pubkey,
owner=instruction.keys[1].pubkey,
market=instruction.keys[2].pubkey,
market_authority=market_authority,
)


def initialize_market(params: InitializeMarketParams) -> TransactionInstruction:
"""Generate a transaction instruction to initialize a Serum market."""
return TransactionInstruction(
Expand Down Expand Up @@ -519,7 +578,10 @@ def match_orders(params: MatchOrdersParams) -> TransactionInstruction:
],
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=InstructionType.MATCH_ORDER, args=dict(limit=params.limit))
dict(
instruction_type=InstructionType.MATCH_ORDER,
args=dict(limit=params.limit),
)
),
)

Expand All @@ -534,7 +596,10 @@ def consume_events(params: ConsumeEventsParams) -> TransactionInstruction:
keys=keys,
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=InstructionType.CONSUME_EVENTS, args=dict(limit=params.limit))
dict(
instruction_type=InstructionType.CONSUME_EVENTS,
args=dict(limit=params.limit),
)
),
)

Expand Down Expand Up @@ -582,7 +647,9 @@ def settle_funds(params: SettleFundsParams) -> TransactionInstruction:
)


def cancel_order_by_client_id(params: CancelOrderByClientIDParams) -> TransactionInstruction:
def cancel_order_by_client_id(
params: CancelOrderByClientIDParams,
) -> TransactionInstruction:
"""Generate a transaction instruction to cancel order by client id."""
return TransactionInstruction(
keys=[
Expand Down Expand Up @@ -668,7 +735,9 @@ def cancel_order_v2(params: CancelOrderV2Params) -> TransactionInstruction:
)


def cancel_order_by_client_id_v2(params: CancelOrderByClientIDV2Params) -> TransactionInstruction:
def cancel_order_by_client_id_v2(
params: CancelOrderByClientIDV2Params,
) -> TransactionInstruction:
"""Generate a transaction instruction to cancel order by client id."""
return TransactionInstruction(
keys=[
Expand All @@ -689,3 +758,35 @@ def cancel_order_by_client_id_v2(params: CancelOrderByClientIDV2Params) -> Trans
)
),
)


def close_open_orders(params: CloseOpenOrdersParams) -> TransactionInstruction:
"""Generate a transaction instruction to close open orders account."""
return TransactionInstruction(
keys=[
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.owner, is_signer=True, is_writable=False),
AccountMeta(pubkey=params.sol_wallet, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
],
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.CLOSE_OPEN_ORDERS, args=dict())),
)


def init_open_orders(params: InitOpenOrdersParams) -> TransactionInstruction:
"""Generate a transaction instruction to initialize open orders account."""
touched_keys = [
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
AccountMeta(pubkey=params.owner, is_signer=True, is_writable=False),
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
]
if params.market_authority:
touched_keys.append(
AccountMeta(pubkey=params.market_authority, is_signer=False, is_writable=False),
)
return TransactionInstruction(
keys=touched_keys,
program_id=params.program_id,
data=INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.INIT_OPEN_ORDERS, args=dict())),
)
2 changes: 1 addition & 1 deletion pyserum/market/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .market import Market # noqa: F401
from .async_market import AsyncMarket # noqa: F401
from .market import Market # noqa: F401
from .orderbook import OrderBook # noqa: F401
from .state import MarketState as State # noqa: F401
6 changes: 3 additions & 3 deletions pyserum/market/async_market.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
from solana.rpc.types import RPCResponse, TxOpts
from solana.transaction import Transaction

from pyserum import instructions
import pyserum.market.types as t
from pyserum import instructions

from .._layouts.open_orders import OPEN_ORDERS_LAYOUT
from ..enums import OrderType, Side
from ..async_open_orders_account import AsyncOpenOrdersAccount
from ..async_utils import load_bytes_data
from ..enums import OrderType, Side
from ._internal.queue import decode_event_queue, decode_request_queue
from .core import MarketCore
from .orderbook import OrderBook
from .state import MarketState
from .core import MarketCore

LAMPORTS_PER_SOL = 1000000000

Expand Down
7 changes: 3 additions & 4 deletions pyserum/market/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@
from solana.system_program import CreateAccountParams, create_account
from solana.transaction import Transaction, TransactionInstruction
from spl.token.constants import ACCOUNT_LEN, TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT
from spl.token.instructions import CloseAccountParams
from spl.token.instructions import InitializeAccountParams, close_account, initialize_account
from spl.token.instructions import CloseAccountParams, InitializeAccountParams, close_account, initialize_account

from pyserum import instructions
import pyserum.market.types as t
from pyserum import instructions

from ..async_open_orders_account import AsyncOpenOrdersAccount
from ..enums import OrderType, SelfTradeBehavior, Side
from ..open_orders_account import OpenOrdersAccount, make_create_account_instruction
from ..async_open_orders_account import AsyncOpenOrdersAccount
from ._internal.queue import decode_event_queue
from .orderbook import OrderBook
from .state import MarketState
Expand Down
4 changes: 2 additions & 2 deletions pyserum/market/market.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
from solana.rpc.types import RPCResponse, TxOpts
from solana.transaction import Transaction

from pyserum import instructions
import pyserum.market.types as t
from pyserum import instructions

from .._layouts.open_orders import OPEN_ORDERS_LAYOUT
from ..enums import OrderType, Side
from ..open_orders_account import OpenOrdersAccount
from ..utils import load_bytes_data
from ._internal.queue import decode_event_queue, decode_request_queue
from .core import MarketCore
from .orderbook import OrderBook
from .state import MarketState
from .core import MarketCore

LAMPORTS_PER_SOL = 1000000000

Expand Down
2 changes: 1 addition & 1 deletion pyserum/market/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from solana.rpc.api import Client
from solana.rpc.async_api import AsyncClient

from pyserum import utils, async_utils
from pyserum import async_utils, utils

from .._layouts.market import MARKET_LAYOUT
from .types import AccountFlags
Expand Down
2 changes: 1 addition & 1 deletion pyserum/open_orders_account.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import base64
from typing import List, NamedTuple, TypeVar, Type, Tuple
from typing import List, NamedTuple, Tuple, Type, TypeVar

from solana.publickey import PublicKey
from solana.rpc.api import Client
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import Dict
import asyncio
from typing import Dict

import pytest
from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.rpc.api import Client
from solana.rpc.async_api import AsyncClient

from pyserum.connection import conn
from pyserum.async_connection import async_conn
from pyserum.connection import conn


@pytest.mark.integration
Expand Down
Loading

0 comments on commit d0938e9

Please sign in to comment.