Add load_orders_for_owner and get_live_markets (#52)
* true list of live markets and open orders * linting fix * remove unused req file * moar lints * fixing commitment with solana api * using chain * MOAR LINT * Revert layout changes * Revert state.py changes * Move get_live_markets * Format and lint * Add test for get_live_markets * Update README.md * Address prior feedback Co-authored-by: gitjcs <julien.collardseguin@gmail.com>
This commit is contained in:
parent
fb219b9207
commit
722935cb5f
19
README.md
19
README.md
|
@ -7,11 +7,24 @@ Status](https://github.com/serum-community/pyserum/workflows/CI/badge.svg)](http
|
|||
Python client library for interacting with the [Project Serum](https://projectserum.com/) DEX.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
pip install pyserum
|
||||
```
|
||||
|
||||
## Get Started
|
||||
## Getting Started
|
||||
|
||||
### Mainnet Market Addresses
|
||||
|
||||
```python
|
||||
from pyserum.connection import get_live_markets
|
||||
print(get_live_markets())
|
||||
```
|
||||
|
||||
The source of truth of the market address can be found
|
||||
[here](https://github.com/project-serum/serum-js/blob/master/src/tokens_and_markets.ts).
|
||||
|
||||
### Get Orderbook
|
||||
|
||||
```python
|
||||
from pyserum.connection import conn
|
||||
|
@ -38,10 +51,6 @@ for bid in bids:
|
|||
bid.order_id, bid.info.price, bid.info.size))
|
||||
```
|
||||
|
||||
### Market Addresses in Main Net
|
||||
|
||||
The source of truth of the market address can be found [here](https://github.com/project-serum/serum-js/blob/master/src/tokens_and_markets.ts). Feel free to open a PR if the following addresses needs modification or addition.
|
||||
|
||||
## Development
|
||||
|
||||
### Setup
|
||||
|
|
|
@ -1,6 +1,33 @@
|
|||
from solana.rpc.api import Client
|
||||
import json
|
||||
|
||||
from solana.rpc.api import Client as conn # pylint: disable=unused-import # noqa:F401
|
||||
from solana.rpc.providers.http import requests
|
||||
|
||||
from .market.types import MarketInfo
|
||||
|
||||
|
||||
def conn(endpoint: str) -> Client:
|
||||
"""RPC client connection to interact with the Serum Dex."""
|
||||
return Client(endpoint)
|
||||
def get_live_markets():
|
||||
url = "https://raw.githubusercontent.com/project-serum/serum-js/master/src/tokens_and_markets.ts"
|
||||
resp = requests.get(url)
|
||||
page = resp.text
|
||||
|
||||
# Turn this JS into json
|
||||
data = page.split("MARKETS:")[1].split("}> = ")[1].split(";")[0]
|
||||
data = data.replace(" new PublicKey(", "").replace(")", "")
|
||||
for col in ["name", "address", "programId", "deprecated"]:
|
||||
data = data.replace(col, '"{}"'.format(col))
|
||||
data = data.replace("'", '"')
|
||||
data = data.replace(" ", "")
|
||||
data = data.replace("//NewUSDCmarkets", "")
|
||||
data = data.replace("\n", "")
|
||||
data = data.replace(",}", "}")
|
||||
data = data.replace(",]", "]")
|
||||
data = json.loads(data)
|
||||
|
||||
markets = []
|
||||
for raw in data:
|
||||
if raw["deprecated"]:
|
||||
continue
|
||||
markets.append(MarketInfo(name=raw["name"], address=raw["address"], program_id=raw["programId"]))
|
||||
|
||||
return markets
|
||||
|
|
|
@ -10,7 +10,8 @@ from spl.token.constants import TOKEN_PROGRAM_ID # type: ignore # TODO: Fix and
|
|||
from ._layouts.instructions import INSTRUCTIONS_LAYOUT, InstructionType
|
||||
from .enums import OrderType, Side
|
||||
|
||||
DEFAULT_DEX_PROGRAM_ID = PublicKey("4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn")
|
||||
# V2
|
||||
DEFAULT_DEX_PROGRAM_ID = PublicKey("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o")
|
||||
|
||||
|
||||
class InitializeMarketParams(NamedTuple):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""Market module to interact with Serum DEX."""
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
|
@ -77,12 +78,22 @@ class Market:
|
|||
return OrderBook.from_bytes(self.state, bytes_data)
|
||||
|
||||
def load_asks(self) -> OrderBook:
|
||||
"""Load the Ask order book."""
|
||||
"""Load the ask order book."""
|
||||
bytes_data = load_bytes_data(self.state.asks(), self._conn)
|
||||
return OrderBook.from_bytes(self.state, bytes_data)
|
||||
|
||||
def load_orders_for_owner(self) -> List[t.Order]:
|
||||
raise NotImplementedError("load_orders_for_owner not implemented")
|
||||
def load_orders_for_owner(self, owner_address: PublicKey) -> List[t.Order]:
|
||||
"""Load orders for owner."""
|
||||
bids = self.load_bids()
|
||||
asks = self.load_asks()
|
||||
open_orders_accounts = self.find_open_orders_accounts_for_owner(owner_address)
|
||||
if not open_orders_accounts:
|
||||
return []
|
||||
|
||||
all_orders = itertools.chain(bids.orders(), asks.orders())
|
||||
open_orders_addresses = {str(o.address) for o in open_orders_accounts}
|
||||
orders = [o for o in all_orders if str(o.open_order_address) in open_orders_addresses]
|
||||
return orders
|
||||
|
||||
def load_base_token_for_owner(self):
|
||||
raise NotImplementedError("load_base_token_for_owner not implemented")
|
||||
|
|
|
@ -132,3 +132,12 @@ class Event(NamedTuple):
|
|||
""""""
|
||||
client_order_id: int
|
||||
""""""
|
||||
|
||||
|
||||
class MarketInfo(NamedTuple):
|
||||
name: str
|
||||
""""""
|
||||
address: PublicKey
|
||||
""""""
|
||||
program_id: PublicKey
|
||||
""""""
|
||||
|
|
|
@ -5,7 +5,8 @@ from typing import List, NamedTuple, Sequence
|
|||
|
||||
from solana.publickey import PublicKey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.types import MemcmpOpts
|
||||
from solana.rpc.commitment import Recent
|
||||
from solana.rpc.types import Commitment, MemcmpOpts
|
||||
from solana.system_program import CreateAccountParams, create_account
|
||||
from solana.transaction import TransactionInstruction
|
||||
|
||||
|
@ -73,7 +74,7 @@ class OpenOrdersAccount:
|
|||
|
||||
@staticmethod
|
||||
def find_for_market_and_owner(
|
||||
conn: Client, market: PublicKey, owner: PublicKey, program_id: PublicKey
|
||||
conn: Client, market: PublicKey, owner: PublicKey, program_id: PublicKey, commitment: Commitment = Recent
|
||||
) -> List[OpenOrdersAccount]:
|
||||
filters = [
|
||||
MemcmpOpts(
|
||||
|
@ -85,9 +86,12 @@ class OpenOrdersAccount:
|
|||
bytes=str(owner),
|
||||
),
|
||||
]
|
||||
|
||||
resp = conn.get_program_accounts(
|
||||
program_id, encoding="base64", memcmp_opts=filters, data_size=OPEN_ORDERS_LAYOUT.sizeof()
|
||||
program_id,
|
||||
commitment=commitment,
|
||||
encoding="base64",
|
||||
memcmp_opts=filters,
|
||||
data_size=OPEN_ORDERS_LAYOUT.sizeof(),
|
||||
)
|
||||
accounts = []
|
||||
for account in resp["result"]:
|
||||
|
@ -101,6 +105,7 @@ class OpenOrdersAccount:
|
|||
lamports=int(account_details["lamports"]),
|
||||
)
|
||||
)
|
||||
|
||||
return [OpenOrdersAccount.from_bytes(account.public_key, account.data) for account in accounts]
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from pyserum.connection import get_live_markets
|
||||
from pyserum.market.types import MarketInfo
|
||||
|
||||
|
||||
def test_get_live_markets():
|
||||
assert all(isinstance(market_info, MarketInfo) for market_info in get_live_markets())
|
Loading…
Reference in New Issue