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:
Michael Huang 2020-10-29 18:17:29 -05:00 committed by GitHub
parent fb219b9207
commit 722935cb5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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")

View File

@ -132,3 +132,12 @@ class Event(NamedTuple):
""""""
client_order_id: int
""""""
class MarketInfo(NamedTuple):
name: str
""""""
address: PublicKey
""""""
program_id: PublicKey
""""""

View File

@ -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

View File

@ -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())