Compare commits
6 Commits
5d91691480
...
1e173beb7e
Author | SHA1 | Date |
---|---|---|
waterquarks | 1e173beb7e | |
waterquarks | 6ccb0e3b44 | |
Ennio Nasca | cbb92d96b2 | |
Ennio Nasca | 2dc5b3a5ce | |
Ennio Nasca | 595946a215 | |
Ennio Nasca | 74152dd8a2 |
26
.codecov.yml
26
.codecov.yml
|
@ -1,26 +0,0 @@
|
|||
codecov:
|
||||
notify:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1.5%
|
||||
patch: no
|
||||
changes: no
|
||||
|
||||
comment:
|
||||
layout: "header, diff"
|
||||
behavior: default
|
||||
require_changes: no
|
||||
|
||||
ignore:
|
||||
- tests
|
||||
- docs/conf.py
|
||||
- setup.py
|
|
@ -1,41 +0,0 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Docs
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
push:
|
||||
branches: [alpha]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
- uses: dschep/install-pipenv-action@v1
|
||||
|
||||
- name: Doc dependencies
|
||||
run: pipenv lock -r > requirements.txt
|
||||
|
||||
- name: Sphinx Build
|
||||
uses: ammaraskar/sphinx-action@0.4
|
||||
with:
|
||||
pre-build-command: pip install sphinxemoji
|
||||
docs-folder: .
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./_build/html
|
||||
force_orphan: true
|
|
@ -1,40 +0,0 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: DEX Integration
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the alpha branch
|
||||
on:
|
||||
push:
|
||||
branches: [alpha, master]
|
||||
pull_request:
|
||||
branches: [alpha, master]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
test:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- name: Install pipenv
|
||||
uses: dschep/install-pipenv-action@v1
|
||||
|
||||
- name: Install test dependencies
|
||||
run: pipenv install --skip-lock pytest
|
||||
|
||||
- name: Run integration tests
|
||||
run: scripts/run_int_tests.sh
|
||||
|
||||
- name: Run async integration tests
|
||||
run: scripts/run_async_int_tests.sh
|
|
@ -1,78 +0,0 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
push:
|
||||
branches: [alpha, master]
|
||||
pull_request:
|
||||
branches: [alpha, master]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
test:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- name: Install pipenv
|
||||
uses: dschep/install-pipenv-action@v1
|
||||
|
||||
- name: Install CI dependencies
|
||||
run: |
|
||||
pipenv run install-ci-deps
|
||||
|
||||
- name: Run linters
|
||||
run: |
|
||||
pipenv run black --check --diff --line-length=120 setup.py pyserum tests
|
||||
pipenv run pylint --rcfile=.pylintrc setup.py pyserum tests
|
||||
pipenv run flake8 setup.py pyserum tests
|
||||
pipenv run mypy pyserum
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
pipenv run pytest -v -m "not integration and not async_integration"
|
||||
|
||||
coverage:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- name: Install pipenv
|
||||
uses: dschep/install-pipenv-action@v1
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
pipenv install --dev --skip-lock pytest pytest-cov
|
||||
scripts/run_coverage.sh
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
# You may pin to the exact commit or the version.
|
||||
# uses: codecov/codecov-action@6004246f47ab62d32be025ce173b241cd84ac58e
|
||||
uses: codecov/codecov-action@v1.0.13
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
# Path to coverage file to upload
|
||||
file: ./coverage.xml
|
||||
env_vars: OS,PYTHON
|
||||
# Specify whether or not CI build should fail if Codecov runs into an error during upload
|
||||
fail_ci_if_error: true
|
|
@ -1,3 +1,5 @@
|
|||
.idea
|
||||
|
||||
# notebook checkpoints
|
||||
.ipynb_checkpoints
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[pydocstyle]
|
||||
ignore=D401,D203,D213
|
598
.pylintrc
598
.pylintrc
|
@ -1,598 +0,0 @@
|
|||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Specify a score threshold to be exceeded before program exits with error.
|
||||
fail-under=10
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=missing-class-docstring,
|
||||
missing-function-docstring,
|
||||
missing-module-docstring,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
bad-continuation,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
deprecated-operator-function,
|
||||
deprecated-urllib-function,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape,
|
||||
duplicate-code
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a score less than or equal to 10. You
|
||||
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
|
||||
# which contain the number of messages in each category, as well as 'statement'
|
||||
# which is the total number of statements analyzed. This score is used by the
|
||||
# global evaluation report (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# The type of string formatting that logging methods do. `old` means using %
|
||||
# formatting, `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it work,
|
||||
# install the python-enchant package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains the private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to the private dictionary (see the
|
||||
# --spelling-private-dict-file option) instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
|
||||
# Regular expression of note tags to take in consideration.
|
||||
#notes-rgx=
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=120
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,
|
||||
dict-separator
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be refused
|
||||
bad-names-rgxs=
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be accepted
|
||||
good-names-rgxs=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=no
|
||||
|
||||
# This flag controls whether the implicit-str-concat should generate a warning
|
||||
# on implicit string concatenation in sequences defined over several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# List of modules that can be imported at any level, not just the top level
|
||||
# one.
|
||||
allow-any-import-level=
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=optparse,tkinter.tix
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled).
|
||||
import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Couples of modules and preferred modules, separated by a comma.
|
||||
preferred-modules=
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp,
|
||||
__post_init__
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "BaseException, Exception".
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"python.envFile": "${workspaceFolder}/.env",
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"python.testing.nosetestsEnabled": false,
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackArgs": ["--check", "--line-length", "120"],
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.pylintArgs": ["--max-line-length", "120"],
|
||||
"python.venvPath": "~/.local/share/virtualenvs/",
|
||||
"editor.formatOnSave": true,
|
||||
"python.languageServer": "Jedi",
|
||||
"python.jediEnabled": true
|
||||
}
|
2
Pipfile
2
Pipfile
|
@ -28,7 +28,7 @@ pytest-asyncio = "*"
|
|||
types-requests = "*"
|
||||
|
||||
[packages]
|
||||
solana = {version = ">=0.27.0"}
|
||||
solana = {version = ">=0.29.0"}
|
||||
construct = "*"
|
||||
flake8 = "*"
|
||||
construct-typing = "*"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
17
README.md
17
README.md
|
@ -1,7 +1,3 @@
|
|||
[![Actions
|
||||
Status](https://github.com/serum-community/pyserum/workflows/CI/badge.svg)](https://github.com/serum-community/pyserum/actions?query=workflow%3ACI)
|
||||
[![Codecov](https://codecov.io/gh/serum-community/pyserum/branch/alpha/graph/badge.svg)](https://codecov.io/gh/serum-community/pyserum/branches/alpha)
|
||||
|
||||
# PySerum
|
||||
|
||||
Python client library for interacting with the [Project Serum](https://projectserum.com/) DEX.
|
||||
|
@ -147,3 +143,16 @@ make notebook
|
|||
This will start a docker container with `solana` image and deploy a serum DEX which you can use for testing.
|
||||
|
||||
The market address, program id, and wallet addresses can be found in the new `crank.log` file after the script runs successfully.
|
||||
|
||||
|
||||
### Release
|
||||
|
||||
To release a new version remember to update the version in the `setup.py` file and then run on the latest commit:
|
||||
|
||||
```sh
|
||||
git tag -a v0.7.0a0 -m "[MESSAGE]"
|
||||
git push origin --tags
|
||||
python setup.py sdist bdist_wheel
|
||||
```
|
||||
|
||||
Go on github and create the new release with the latest tag and upload the artifacts from the `dist` folder.
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
version: '3'
|
||||
services:
|
||||
localnet:
|
||||
image: "solanalabs/solana:v1.4.18"
|
||||
ports:
|
||||
- "8899:8899"
|
||||
- "8900:8900"
|
||||
- "9900:9900"
|
52
docs/conf.py
52
docs/conf.py
|
@ -1,52 +0,0 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "pyserum"
|
||||
copyright = "2020, serum-community"
|
||||
author = "Michael Huang, Leonard Ge"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx.ext.coverage",
|
||||
"sphinx.ext.doctest",
|
||||
"sphinxemoji.sphinxemoji",
|
||||
]
|
||||
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "alabaster"
|
|
@ -1,27 +0,0 @@
|
|||
.. pyserum documentation master file, created by
|
||||
sphinx-quickstart on Sun Oct 4 18:54:35 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to pyserum's documentation!
|
||||
===================================
|
||||
|
||||
|:construction:| |:construction:| |:construction:| |:construction:| |:construction:| **Under Construction!** |:construction:| |:construction:| |:construction:| |:construction:| |:construction:|
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
market
|
||||
instructions
|
||||
|
||||
|:construction:| |:construction:| |:construction:| |:construction:| |:construction:| **Under Construction!** |:construction:| |:construction:| |:construction:| |:construction:| |:construction:|
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
|
@ -1,11 +0,0 @@
|
|||
DEX Instructions
|
||||
================
|
||||
|
||||
.. automodule:: pyserum.instructions
|
||||
:members:
|
||||
|
||||
Enums
|
||||
=====
|
||||
|
||||
.. automodule:: pyserum.enums
|
||||
:members:
|
|
@ -1,25 +0,0 @@
|
|||
Serum Market
|
||||
============
|
||||
|
||||
.. automodule:: pyserum.market
|
||||
|
||||
.. autoclass:: pyserum.market.Market
|
||||
:members:
|
||||
|
||||
Market State
|
||||
------------
|
||||
|
||||
.. automodule:: pyserum.market.state
|
||||
|
||||
.. autoclass:: pyserum.market.state.MarketState
|
||||
:members:
|
||||
|
||||
Orderbook
|
||||
---------
|
||||
.. automodule:: pyserum.market.orderbook
|
||||
:members:
|
||||
|
||||
Types
|
||||
-----
|
||||
.. automodule:: pyserum.market.types
|
||||
:members:
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from solana.rpc.api import Client\n",
|
||||
"from solana.publickey import PublicKey\n",
|
||||
"\n",
|
||||
"local_client = Client()\n",
|
||||
"local_dex = PublicKey(\"35xWbYPmt7hzyjsmJ9m3hZekN63mmeQDTygd7i8zudiy\")\n",
|
||||
"local_client.get_account_info(local_dex)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pyserum.market import Market\n",
|
||||
"\n",
|
||||
"market = Market.load(\"https://api.mainnet-beta.solana.com\", \"CAgAeMD7quTdnr6RPa7JySQpjf3irAmefYNdTb6anemq\", None)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"assert market is not None\n",
|
||||
"assert isinstance(market, Market)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"market"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"asks = market.load_asks()\n",
|
||||
"asks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pyserum.market import Market\n",
|
||||
"\n",
|
||||
"endpoint = \"https://api.mainnet-beta.solana.com/\"\n",
|
||||
"market_address = \"CAgAeMD7quTdnr6RPa7JySQpjf3irAmefYNdTb6anemq\" # Address for BTC/USDC\n",
|
||||
"\n",
|
||||
"# Load the given market\n",
|
||||
"market = Market.load(endpoint, market_address, None)\n",
|
||||
"asks = market.load_asks()\n",
|
||||
"# Show all current ask order\n",
|
||||
"print(\"Ask Orders:\")\n",
|
||||
"for ask in asks:\n",
|
||||
" print(\"Order id: %d, price: %f, size: %f.\" % (\n",
|
||||
" ask.order_id, ask.order_info.price, ask.order_info.size))\n",
|
||||
"\n",
|
||||
"print(\"\\n\")\n",
|
||||
"# Show all current bid order\n",
|
||||
"print(\"Bid Orders:\")\n",
|
||||
"bids = market.load_bids()\n",
|
||||
"for bid in bids:\n",
|
||||
" print(\"Order id: %d, price: %f, size: %f.\" % (\n",
|
||||
" bid.order_id, bid.order_info.price, bid.order_info.size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"market.load_fills()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
|
@ -1,4 +1,15 @@
|
|||
from construct import BitsInteger, BitsSwapped, BitStruct, 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
|
||||
|
|
|
@ -33,7 +33,9 @@ class NodeType(IntEnum):
|
|||
|
||||
# Different node types, we pad it all to size of 68 bytes.
|
||||
UNINTIALIZED = cStruct(Padding(68))
|
||||
INNER_NODE = cStruct("prefix_len" / Int32ul, "key" / KEY, "children" / Int32ul[2], Padding(40))
|
||||
INNER_NODE = cStruct(
|
||||
"prefix_len" / Int32ul, "key" / KEY, "children" / Int32ul[2], Padding(40)
|
||||
)
|
||||
LEAF_NODE = cStruct(
|
||||
"owner_slot" / Int8ul,
|
||||
"fee_tier" / Int8ul,
|
||||
|
@ -61,6 +63,14 @@ SLAB_NODE_LAYOUT = cStruct(
|
|||
),
|
||||
)
|
||||
|
||||
SLAB_LAYOUT = cStruct("header" / SLAB_HEADER_LAYOUT, "nodes" / SLAB_NODE_LAYOUT[lambda this: this.header.bump_index])
|
||||
SLAB_LAYOUT = cStruct(
|
||||
"header" / SLAB_HEADER_LAYOUT,
|
||||
"nodes" / SLAB_NODE_LAYOUT[lambda this: this.header.bump_index],
|
||||
)
|
||||
|
||||
ORDER_BOOK_LAYOUT = cStruct(Padding(5), "account_flags" / ACCOUNT_FLAGS_LAYOUT, "slab_layout" / SLAB_LAYOUT, Padding(7))
|
||||
ORDER_BOOK_LAYOUT = cStruct(
|
||||
Padding(5),
|
||||
"account_flags" / ACCOUNT_FLAGS_LAYOUT,
|
||||
"slab_layout" / SLAB_LAYOUT,
|
||||
Padding(7),
|
||||
)
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
from typing import List
|
||||
|
||||
import httpx
|
||||
from solana.rpc.async_api import AsyncClient as async_conn # pylint: disable=unused-import # noqa:F401
|
||||
from solana.rpc.async_api import (
|
||||
AsyncClient as async_conn,
|
||||
) # pylint: disable=unused-import # noqa:F401
|
||||
|
||||
from .connection import LIVE_MARKETS_URL, TOKEN_MINTS_URL, parse_live_markets, parse_token_mints
|
||||
from .connection import (
|
||||
LIVE_MARKETS_URL,
|
||||
TOKEN_MINTS_URL,
|
||||
parse_live_markets,
|
||||
parse_token_mints,
|
||||
)
|
||||
from .market.types import MarketInfo, TokenInfo
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import List
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
from solana.rpc.commitment import Processed
|
||||
from solana.rpc.types import Commitment
|
||||
|
@ -16,9 +16,9 @@ class AsyncOpenOrdersAccount(_OpenOrdersAccountCore):
|
|||
async def find_for_market_and_owner( # pylint: disable=too-many-arguments
|
||||
cls,
|
||||
conn: AsyncClient,
|
||||
market: PublicKey,
|
||||
owner: PublicKey,
|
||||
program_id: PublicKey,
|
||||
market: Pubkey,
|
||||
owner: Pubkey,
|
||||
program_id: Pubkey,
|
||||
commitment: Commitment = Processed,
|
||||
) -> List[AsyncOpenOrdersAccount]:
|
||||
args = cls._build_get_program_accounts_args(
|
||||
|
@ -29,6 +29,6 @@ class AsyncOpenOrdersAccount(_OpenOrdersAccountCore):
|
|||
|
||||
@classmethod
|
||||
async def load(cls, conn: AsyncClient, address: str) -> AsyncOpenOrdersAccount:
|
||||
addr_pub_key = PublicKey(address)
|
||||
addr_pub_key = Pubkey.from_string(address)
|
||||
bytes_data = await load_bytes_data(addr_pub_key, conn)
|
||||
return cls.from_bytes(addr_pub_key, bytes_data)
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
from spl.token.constants import WRAPPED_SOL_MINT
|
||||
|
||||
from pyserum.utils import parse_bytes_data, parse_mint_decimals
|
||||
|
||||
|
||||
async def load_bytes_data(addr: PublicKey, conn: AsyncClient) -> bytes:
|
||||
async def load_bytes_data(addr: Pubkey, conn: AsyncClient) -> bytes:
|
||||
res = await conn.get_account_info(addr)
|
||||
return parse_bytes_data(res)
|
||||
|
||||
|
||||
async def get_mint_decimals(conn: AsyncClient, mint_pub_key: PublicKey) -> int:
|
||||
async def get_mint_decimals(conn: AsyncClient, mint_pub_key: Pubkey) -> int:
|
||||
"""Get the mint decimals for a token mint"""
|
||||
if mint_pub_key == WRAPPED_SOL_MINT:
|
||||
return 9
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Any, Dict, List
|
||||
|
||||
import requests
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client as conn # pylint: disable=unused-import # noqa:F401
|
||||
|
||||
from .market.types import MarketInfo, TokenInfo
|
||||
|
@ -12,12 +12,17 @@ TOKEN_MINTS_URL = "https://raw.githubusercontent.com/project-serum/serum-ts/mast
|
|||
|
||||
def parse_live_markets(data: List[Dict[str, Any]]) -> List[MarketInfo]:
|
||||
return [
|
||||
MarketInfo(name=m["name"], address=m["address"], program_id=m["programId"]) for m in data if not m["deprecated"]
|
||||
MarketInfo(name=m["name"], address=m["address"], program_id=m["programId"])
|
||||
for m in data
|
||||
if not m["deprecated"]
|
||||
]
|
||||
|
||||
|
||||
def parse_token_mints(data: List[Dict[str, str]]) -> List[TokenInfo]:
|
||||
return [TokenInfo(name=t["name"], address=PublicKey(t["address"])) for t in data]
|
||||
return [
|
||||
TokenInfo(name=t["name"], address=Pubkey.from_string(t["address"]))
|
||||
for t in data
|
||||
]
|
||||
|
||||
|
||||
def get_live_markets() -> List[MarketInfo]:
|
||||
|
|
|
@ -2,39 +2,42 @@
|
|||
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 solders.sysvar import RENT
|
||||
from solana.transaction import AccountMeta
|
||||
from solders.instruction import Instruction
|
||||
from solana.utils.validate import validate_instruction_keys, validate_instruction_type
|
||||
from solders.pubkey import Pubkey
|
||||
from spl.token.constants import TOKEN_PROGRAM_ID
|
||||
|
||||
from ._layouts.instructions import INSTRUCTIONS_LAYOUT, InstructionType
|
||||
from .enums import OrderType, SelfTradeBehavior, Side
|
||||
|
||||
# V3
|
||||
DEFAULT_DEX_PROGRAM_ID = PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
||||
DEFAULT_DEX_PROGRAM_ID = Pubkey.from_string(
|
||||
"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
|
||||
)
|
||||
|
||||
|
||||
class InitializeMarketParams(NamedTuple):
|
||||
"""Initalize market params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
bids: PublicKey
|
||||
bids: Pubkey
|
||||
""""""
|
||||
asks: PublicKey
|
||||
asks: Pubkey
|
||||
""""""
|
||||
base_vault: PublicKey
|
||||
base_vault: Pubkey
|
||||
""""""
|
||||
quote_vault: PublicKey
|
||||
quote_vault: Pubkey
|
||||
""""""
|
||||
base_mint: PublicKey
|
||||
base_mint: Pubkey
|
||||
""""""
|
||||
quote_mint: PublicKey
|
||||
quote_mint: Pubkey
|
||||
""""""
|
||||
base_lot_size: int
|
||||
""""""
|
||||
|
@ -46,25 +49,25 @@ class InitializeMarketParams(NamedTuple):
|
|||
""""""
|
||||
quote_dust_threshold: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
|
||||
|
||||
class NewOrderParams(NamedTuple):
|
||||
"""New order params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
payer: PublicKey
|
||||
payer: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
base_vault: PublicKey
|
||||
base_vault: Pubkey
|
||||
""""""
|
||||
quote_vault: PublicKey
|
||||
quote_vault: Pubkey
|
||||
""""""
|
||||
side: Side
|
||||
""""""
|
||||
|
@ -76,58 +79,58 @@ class NewOrderParams(NamedTuple):
|
|||
""""""
|
||||
client_id: int = 0
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class MatchOrdersParams(NamedTuple):
|
||||
"""Match order params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
bids: PublicKey
|
||||
bids: Pubkey
|
||||
""""""
|
||||
asks: PublicKey
|
||||
asks: Pubkey
|
||||
""""""
|
||||
base_vault: PublicKey
|
||||
base_vault: Pubkey
|
||||
""""""
|
||||
quote_vault: PublicKey
|
||||
quote_vault: Pubkey
|
||||
""""""
|
||||
limit: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class ConsumeEventsParams(NamedTuple):
|
||||
"""Consume events params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
open_orders_accounts: List[PublicKey]
|
||||
open_orders_accounts: List[Pubkey]
|
||||
""""""
|
||||
limit: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class CancelOrderParams(NamedTuple):
|
||||
"""Cancel order params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
side: Side
|
||||
""""""
|
||||
|
@ -135,71 +138,71 @@ class CancelOrderParams(NamedTuple):
|
|||
""""""
|
||||
open_orders_slot: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class CancelOrderByClientIDParams(NamedTuple):
|
||||
"""Cancel order by client ID params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
client_id: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class SettleFundsParams(NamedTuple):
|
||||
"""Settle fund params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
base_vault: PublicKey
|
||||
base_vault: Pubkey
|
||||
""""""
|
||||
quote_vault: PublicKey
|
||||
quote_vault: Pubkey
|
||||
""""""
|
||||
base_wallet: PublicKey
|
||||
base_wallet: Pubkey
|
||||
""""""
|
||||
quote_wallet: PublicKey
|
||||
quote_wallet: Pubkey
|
||||
""""""
|
||||
vault_signer: PublicKey
|
||||
vault_signer: Pubkey
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
|
||||
|
||||
class NewOrderV3Params(NamedTuple):
|
||||
"""New order params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
payer: PublicKey
|
||||
payer: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
request_queue: PublicKey
|
||||
request_queue: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
bids: PublicKey
|
||||
bids: Pubkey
|
||||
""""""
|
||||
asks: PublicKey
|
||||
asks: Pubkey
|
||||
""""""
|
||||
base_vault: PublicKey
|
||||
base_vault: Pubkey
|
||||
""""""
|
||||
quote_vault: PublicKey
|
||||
quote_vault: Pubkey
|
||||
""""""
|
||||
side: Side
|
||||
""""""
|
||||
|
@ -217,25 +220,25 @@ class NewOrderV3Params(NamedTuple):
|
|||
""""""
|
||||
client_id: int = 0
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
fee_discount_pubkey: Optional[PublicKey] = None
|
||||
fee_discount_pubkey: Optional[Pubkey] = None
|
||||
|
||||
|
||||
class CancelOrderV2Params(NamedTuple):
|
||||
"""Cancel order params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
bids: PublicKey
|
||||
bids: Pubkey
|
||||
""""""
|
||||
asks: PublicKey
|
||||
asks: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
side: Side
|
||||
""""""
|
||||
|
@ -243,63 +246,63 @@ class CancelOrderV2Params(NamedTuple):
|
|||
""""""
|
||||
open_orders_slot: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class CancelOrderByClientIDV2Params(NamedTuple):
|
||||
"""Cancel order by client ID params."""
|
||||
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
bids: PublicKey
|
||||
bids: Pubkey
|
||||
""""""
|
||||
asks: PublicKey
|
||||
asks: Pubkey
|
||||
""""""
|
||||
event_queue: PublicKey
|
||||
event_queue: Pubkey
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
client_id: int
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class CloseOpenOrdersParams(NamedTuple):
|
||||
"""Cancel order by client ID params."""
|
||||
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
sol_wallet: PublicKey
|
||||
sol_wallet: Pubkey
|
||||
""""""
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
class InitOpenOrdersParams(NamedTuple):
|
||||
"""Cancel order by client ID params."""
|
||||
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
""""""
|
||||
market: PublicKey
|
||||
market: Pubkey
|
||||
""""""
|
||||
market_authority: Optional[PublicKey] = None
|
||||
market_authority: Optional[Pubkey] = None
|
||||
""""""
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID
|
||||
""""""
|
||||
|
||||
|
||||
def __parse_and_validate_instruction(
|
||||
instruction: TransactionInstruction, instruction_type: InstructionType
|
||||
instruction: Instruction, instruction_type: InstructionType
|
||||
) -> Container:
|
||||
instruction_type_to_length_map: Dict[InstructionType, int] = {
|
||||
InstructionType.INITIALIZE_MARKET: 9,
|
||||
|
@ -315,27 +318,31 @@ def __parse_and_validate_instruction(
|
|||
InstructionType.CLOSE_OPEN_ORDERS: 4,
|
||||
InstructionType.INIT_OPEN_ORDERS: 3,
|
||||
}
|
||||
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)
|
||||
validate_instruction_type(data, instruction_type)
|
||||
return data
|
||||
|
||||
|
||||
def decode_initialize_market(
|
||||
instruction: TransactionInstruction,
|
||||
instruction: Instruction,
|
||||
) -> InitializeMarketParams:
|
||||
"""Decode an instialize market instruction and retrieve the instruction params."""
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.INITIALIZE_MARKET)
|
||||
data = __parse_and_validate_instruction(
|
||||
instruction, InstructionType.INITIALIZE_MARKET
|
||||
)
|
||||
return InitializeMarketParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
request_queue=instruction.keys[1].pubkey,
|
||||
event_queue=instruction.keys[2].pubkey,
|
||||
bids=instruction.keys[3].pubkey,
|
||||
asks=instruction.keys[4].pubkey,
|
||||
base_vault=instruction.keys[5].pubkey,
|
||||
quote_vault=instruction.keys[6].pubkey,
|
||||
base_mint=instruction.keys[7].pubkey,
|
||||
quote_mint=instruction.keys[8].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
request_queue=instruction.accounts[1].pubkey,
|
||||
event_queue=instruction.accounts[2].pubkey,
|
||||
bids=instruction.accounts[3].pubkey,
|
||||
asks=instruction.accounts[4].pubkey,
|
||||
base_vault=instruction.accounts[5].pubkey,
|
||||
quote_vault=instruction.accounts[6].pubkey,
|
||||
base_mint=instruction.accounts[7].pubkey,
|
||||
quote_mint=instruction.accounts[8].pubkey,
|
||||
base_lot_size=data.args.base_lot_size,
|
||||
quote_lot_size=data.args.quote_lot_size,
|
||||
fee_rate_bps=data.args.fee_rate_bps,
|
||||
|
@ -345,16 +352,16 @@ def decode_initialize_market(
|
|||
)
|
||||
|
||||
|
||||
def decode_new_order(instruction: TransactionInstruction) -> NewOrderParams:
|
||||
def decode_new_order(instruction: Instruction) -> NewOrderParams:
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.NEW_ORDER)
|
||||
return NewOrderParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
open_orders=instruction.keys[1].pubkey,
|
||||
request_queue=instruction.keys[2].pubkey,
|
||||
payer=instruction.keys[3].pubkey,
|
||||
owner=instruction.keys[4].pubkey,
|
||||
base_vault=instruction.keys[5].pubkey,
|
||||
quote_vault=instruction.keys[6].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
open_orders=instruction.accounts[1].pubkey,
|
||||
request_queue=instruction.accounts[2].pubkey,
|
||||
payer=instruction.accounts[3].pubkey,
|
||||
owner=instruction.accounts[4].pubkey,
|
||||
base_vault=instruction.accounts[5].pubkey,
|
||||
quote_vault=instruction.accounts[6].pubkey,
|
||||
side=data.args.side,
|
||||
limit_price=data.args.limit_price,
|
||||
max_quantity=data.args.max_quantity,
|
||||
|
@ -363,86 +370,88 @@ def decode_new_order(instruction: TransactionInstruction) -> NewOrderParams:
|
|||
)
|
||||
|
||||
|
||||
def decode_match_orders(instruction: TransactionInstruction) -> MatchOrdersParams:
|
||||
def decode_match_orders(instruction: Instruction) -> MatchOrdersParams:
|
||||
"""Decode a match orders instruction and retrieve the instruction params."""
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.MATCH_ORDER)
|
||||
return MatchOrdersParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
request_queue=instruction.keys[1].pubkey,
|
||||
event_queue=instruction.keys[2].pubkey,
|
||||
bids=instruction.keys[3].pubkey,
|
||||
asks=instruction.keys[4].pubkey,
|
||||
base_vault=instruction.keys[5].pubkey,
|
||||
quote_vault=instruction.keys[6].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
request_queue=instruction.accounts[1].pubkey,
|
||||
event_queue=instruction.accounts[2].pubkey,
|
||||
bids=instruction.accounts[3].pubkey,
|
||||
asks=instruction.accounts[4].pubkey,
|
||||
base_vault=instruction.accounts[5].pubkey,
|
||||
quote_vault=instruction.accounts[6].pubkey,
|
||||
limit=data.args.limit,
|
||||
)
|
||||
|
||||
|
||||
def decode_consume_events(instruction: TransactionInstruction) -> ConsumeEventsParams:
|
||||
def decode_consume_events(instruction: Instruction) -> ConsumeEventsParams:
|
||||
"""Decode a consume events instruction and retrieve the instruction params."""
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.CONSUME_EVENTS)
|
||||
return ConsumeEventsParams(
|
||||
open_orders_accounts=[a_m.pubkey for a_m in instruction.keys[:-4]],
|
||||
market=instruction.keys[-4].pubkey,
|
||||
event_queue=instruction.keys[-3].pubkey,
|
||||
open_orders_accounts=[a_m.pubkey for a_m in instruction.accounts[:-4]],
|
||||
market=instruction.accounts[-4].pubkey,
|
||||
event_queue=instruction.accounts[-3].pubkey,
|
||||
# NOTE - ignoring pc_fee and coin_fee as unused
|
||||
limit=data.args.limit,
|
||||
)
|
||||
|
||||
|
||||
def decode_cancel_order(instruction: TransactionInstruction) -> CancelOrderParams:
|
||||
def decode_cancel_order(instruction: Instruction) -> CancelOrderParams:
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.CANCEL_ORDER)
|
||||
return CancelOrderParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
open_orders=instruction.keys[1].pubkey,
|
||||
request_queue=instruction.keys[2].pubkey,
|
||||
owner=instruction.keys[3].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
open_orders=instruction.accounts[1].pubkey,
|
||||
request_queue=instruction.accounts[2].pubkey,
|
||||
owner=instruction.accounts[3].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_settle_funds(instruction: TransactionInstruction) -> SettleFundsParams:
|
||||
def decode_settle_funds(instruction: Instruction) -> SettleFundsParams:
|
||||
# data = __parse_and_validate_instruction(instruction, InstructionType.SettleFunds)
|
||||
return SettleFundsParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
open_orders=instruction.keys[1].pubkey,
|
||||
owner=instruction.keys[2].pubkey,
|
||||
base_vault=instruction.keys[3].pubkey,
|
||||
quote_vault=instruction.keys[4].pubkey,
|
||||
base_wallet=instruction.keys[5].pubkey,
|
||||
quote_wallet=instruction.keys[6].pubkey,
|
||||
vault_signer=instruction.keys[7].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
open_orders=instruction.accounts[1].pubkey,
|
||||
owner=instruction.accounts[2].pubkey,
|
||||
base_vault=instruction.accounts[3].pubkey,
|
||||
quote_vault=instruction.accounts[4].pubkey,
|
||||
base_wallet=instruction.accounts[5].pubkey,
|
||||
quote_wallet=instruction.accounts[6].pubkey,
|
||||
vault_signer=instruction.accounts[7].pubkey,
|
||||
)
|
||||
|
||||
|
||||
def decode_cancel_order_by_client_id(
|
||||
instruction: TransactionInstruction,
|
||||
instruction: Instruction,
|
||||
) -> CancelOrderByClientIDParams:
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.CANCEL_ORDER_BY_CLIENT_ID)
|
||||
data = __parse_and_validate_instruction(
|
||||
instruction, InstructionType.CANCEL_ORDER_BY_CLIENT_ID
|
||||
)
|
||||
return CancelOrderByClientIDParams(
|
||||
market=instruction.keys[0].pubkey,
|
||||
open_orders=instruction.keys[1].pubkey,
|
||||
request_queue=instruction.keys[2].pubkey,
|
||||
owner=instruction.keys[3].pubkey,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
open_orders=instruction.accounts[1].pubkey,
|
||||
request_queue=instruction.accounts[2].pubkey,
|
||||
owner=instruction.accounts[3].pubkey,
|
||||
client_id=data.args.client_id,
|
||||
)
|
||||
|
||||
|
||||
def decode_new_order_v3(instruction: TransactionInstruction) -> NewOrderV3Params:
|
||||
def decode_new_order_v3(instruction: Instruction) -> NewOrderV3Params:
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.NEW_ORDER_V3)
|
||||
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,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
open_orders=instruction.accounts[1].pubkey,
|
||||
request_queue=instruction.accounts[2].pubkey,
|
||||
event_queue=instruction.accounts[3].pubkey,
|
||||
bids=instruction.accounts[4].pubkey,
|
||||
asks=instruction.accounts[5].pubkey,
|
||||
payer=instruction.accounts[6].pubkey,
|
||||
owner=instruction.accounts[7].pubkey,
|
||||
base_vault=instruction.accounts[8].pubkey,
|
||||
quote_vault=instruction.accounts[9].pubkey,
|
||||
side=data.args.side,
|
||||
limit_price=data.args.limit_price,
|
||||
max_base_quantity=data.args.max_base_quantity,
|
||||
|
@ -454,61 +463,69 @@ def decode_new_order_v3(instruction: TransactionInstruction) -> NewOrderV3Params
|
|||
)
|
||||
|
||||
|
||||
def decode_cancel_order_v2(instruction: TransactionInstruction) -> CancelOrderV2Params:
|
||||
data = __parse_and_validate_instruction(instruction, InstructionType.CANCEL_ORDER_V2)
|
||||
def decode_cancel_order_v2(instruction: Instruction) -> CancelOrderV2Params:
|
||||
data = __parse_and_validate_instruction(
|
||||
instruction, InstructionType.CANCEL_ORDER_V2
|
||||
)
|
||||
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,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
bids=instruction.accounts[1].pubkey,
|
||||
asks=instruction.accounts[2].pubkey,
|
||||
open_orders=instruction.accounts[3].pubkey,
|
||||
owner=instruction.accounts[4].pubkey,
|
||||
event_queue=instruction.accounts[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.CANCEL_ORDER_BY_CLIENT_ID_V2)
|
||||
def decode_cancel_order_by_client_id_v2(
|
||||
instruction: Instruction,
|
||||
) -> CancelOrderByClientIDV2Params:
|
||||
data = __parse_and_validate_instruction(
|
||||
instruction, InstructionType.CANCEL_ORDER_BY_CLIENT_ID_V2
|
||||
)
|
||||
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,
|
||||
market=instruction.accounts[0].pubkey,
|
||||
bids=instruction.accounts[1].pubkey,
|
||||
asks=instruction.accounts[2].pubkey,
|
||||
open_orders=instruction.accounts[3].pubkey,
|
||||
owner=instruction.accounts[4].pubkey,
|
||||
event_queue=instruction.accounts[5].pubkey,
|
||||
client_id=data.args.client_id,
|
||||
)
|
||||
|
||||
|
||||
def decode_close_open_orders(
|
||||
instruction: TransactionInstruction,
|
||||
instruction: Instruction,
|
||||
) -> 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,
|
||||
open_orders=instruction.accounts[0].pubkey,
|
||||
owner=instruction.accounts[1].pubkey,
|
||||
sol_wallet=instruction.accounts[2].pubkey,
|
||||
market=instruction.accounts[3].pubkey,
|
||||
)
|
||||
|
||||
|
||||
def decode_init_open_orders(
|
||||
instruction: TransactionInstruction,
|
||||
instruction: Instruction,
|
||||
) -> InitOpenOrdersParams:
|
||||
market_authority = instruction.keys[-1].pubkey if len(instruction.keys) == 5 else None
|
||||
market_authority = (
|
||||
instruction.accounts[-1].pubkey if len(instruction.accounts) == 5 else None
|
||||
)
|
||||
return InitOpenOrdersParams(
|
||||
open_orders=instruction.keys[0].pubkey,
|
||||
owner=instruction.keys[1].pubkey,
|
||||
market=instruction.keys[2].pubkey,
|
||||
open_orders=instruction.accounts[0].pubkey,
|
||||
owner=instruction.accounts[1].pubkey,
|
||||
market=instruction.accounts[2].pubkey,
|
||||
market_authority=market_authority,
|
||||
)
|
||||
|
||||
|
||||
def initialize_market(params: InitializeMarketParams) -> TransactionInstruction:
|
||||
def initialize_market(params: InitializeMarketParams) -> Instruction:
|
||||
"""Generate a transaction instruction to initialize a Serum market."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
|
||||
AccountMeta(pubkey=params.request_queue, is_signer=False, is_writable=True),
|
||||
AccountMeta(pubkey=params.event_queue, is_signer=False, is_writable=True),
|
||||
|
@ -535,10 +552,10 @@ def initialize_market(params: InitializeMarketParams) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def new_order(params: NewOrderParams) -> TransactionInstruction:
|
||||
def new_order(params: NewOrderParams) -> Instruction:
|
||||
"""Generate a transaction instruction to place new order."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
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),
|
||||
|
@ -547,7 +564,7 @@ def new_order(params: NewOrderParams) -> TransactionInstruction:
|
|||
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),
|
||||
AccountMeta(pubkey=RENT, is_signer=False, is_writable=False),
|
||||
],
|
||||
program_id=params.program_id,
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
|
@ -565,10 +582,10 @@ def new_order(params: NewOrderParams) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def match_orders(params: MatchOrdersParams) -> TransactionInstruction:
|
||||
def match_orders(params: MatchOrdersParams) -> Instruction:
|
||||
"""Generate a transaction instruction to match order."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
AccountMeta(pubkey=params.market, 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),
|
||||
|
@ -587,15 +604,16 @@ def match_orders(params: MatchOrdersParams) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def consume_events(params: ConsumeEventsParams) -> TransactionInstruction:
|
||||
def consume_events(params: ConsumeEventsParams) -> Instruction:
|
||||
"""Generate a transaction instruction to consume market events."""
|
||||
keys = [
|
||||
accounts = [
|
||||
AccountMeta(pubkey=pubkey, is_signer=False, is_writable=True)
|
||||
# NOTE - last two accounts are required for backwards compatibility but are ignored
|
||||
for pubkey in params.open_orders_accounts + (2 * [params.market, params.event_queue])
|
||||
for pubkey in params.open_orders_accounts
|
||||
+ (2 * [params.market, params.event_queue])
|
||||
]
|
||||
return TransactionInstruction(
|
||||
keys=keys,
|
||||
return Instruction(
|
||||
accounts=accounts,
|
||||
program_id=params.program_id,
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
dict(
|
||||
|
@ -606,10 +624,10 @@ def consume_events(params: ConsumeEventsParams) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def cancel_order(params: CancelOrderParams) -> TransactionInstruction:
|
||||
def cancel_order(params: CancelOrderParams) -> Instruction:
|
||||
"""Generate a transaction instruction to cancel order."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
|
||||
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
|
||||
AccountMeta(pubkey=params.request_queue, is_signer=False, is_writable=True),
|
||||
|
@ -630,10 +648,10 @@ def cancel_order(params: CancelOrderParams) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def settle_funds(params: SettleFundsParams) -> TransactionInstruction:
|
||||
def settle_funds(params: SettleFundsParams) -> Instruction:
|
||||
"""Generate a transaction instruction to settle fund."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
AccountMeta(pubkey=params.market, 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),
|
||||
|
@ -645,16 +663,18 @@ def settle_funds(params: SettleFundsParams) -> TransactionInstruction:
|
|||
AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False),
|
||||
],
|
||||
program_id=params.program_id,
|
||||
data=INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.SETTLE_FUNDS, args=dict())),
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.SETTLE_FUNDS, args=dict())
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def cancel_order_by_client_id(
|
||||
params: CancelOrderByClientIDParams,
|
||||
) -> TransactionInstruction:
|
||||
) -> Instruction:
|
||||
"""Generate a transaction instruction to cancel order by client id."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
AccountMeta(pubkey=params.market, is_signer=False, is_writable=False),
|
||||
AccountMeta(pubkey=params.open_orders, is_signer=False, is_writable=True),
|
||||
AccountMeta(pubkey=params.request_queue, is_signer=False, is_writable=True),
|
||||
|
@ -672,9 +692,9 @@ def cancel_order_by_client_id(
|
|||
)
|
||||
|
||||
|
||||
def new_order_v3(params: NewOrderV3Params) -> TransactionInstruction:
|
||||
def new_order_v3(params: NewOrderV3Params) -> Instruction:
|
||||
"""Generate a transaction instruction to place new order."""
|
||||
touched_keys = [
|
||||
touched_accounts = [
|
||||
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),
|
||||
|
@ -686,14 +706,16 @@ def new_order_v3(params: NewOrderV3Params) -> TransactionInstruction:
|
|||
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),
|
||||
AccountMeta(pubkey=RENT, 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),
|
||||
touched_accounts.append(
|
||||
AccountMeta(
|
||||
pubkey=params.fee_discount_pubkey, is_signer=False, is_writable=False
|
||||
),
|
||||
)
|
||||
return TransactionInstruction(
|
||||
keys=touched_keys,
|
||||
return Instruction(
|
||||
accounts=touched_accounts,
|
||||
program_id=params.program_id,
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
dict(
|
||||
|
@ -713,10 +735,10 @@ def new_order_v3(params: NewOrderV3Params) -> TransactionInstruction:
|
|||
)
|
||||
|
||||
|
||||
def cancel_order_v2(params: CancelOrderV2Params) -> TransactionInstruction:
|
||||
def cancel_order_v2(params: CancelOrderV2Params) -> Instruction:
|
||||
"""Generate a transaction instruction to cancel order."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
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),
|
||||
|
@ -739,10 +761,10 @@ def cancel_order_v2(params: CancelOrderV2Params) -> TransactionInstruction:
|
|||
|
||||
def cancel_order_by_client_id_v2(
|
||||
params: CancelOrderByClientIDV2Params,
|
||||
) -> TransactionInstruction:
|
||||
) -> Instruction:
|
||||
"""Generate a transaction instruction to cancel order by client id."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
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),
|
||||
|
@ -762,34 +784,40 @@ def cancel_order_by_client_id_v2(
|
|||
)
|
||||
|
||||
|
||||
def close_open_orders(params: CloseOpenOrdersParams) -> TransactionInstruction:
|
||||
def close_open_orders(params: CloseOpenOrdersParams) -> Instruction:
|
||||
"""Generate a transaction instruction to close open orders account."""
|
||||
return TransactionInstruction(
|
||||
keys=[
|
||||
return Instruction(
|
||||
accounts=[
|
||||
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())),
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.CLOSE_OPEN_ORDERS, args=dict())
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def init_open_orders(params: InitOpenOrdersParams) -> TransactionInstruction:
|
||||
def init_open_orders(params: InitOpenOrdersParams) -> Instruction:
|
||||
"""Generate a transaction instruction to initialize open orders account."""
|
||||
touched_keys = [
|
||||
touched_accounts = [
|
||||
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),
|
||||
AccountMeta(pubkey=SYSVAR_RENT_PUBKEY, is_signer=False, is_writable=False),
|
||||
AccountMeta(pubkey=RENT, is_signer=False, is_writable=False),
|
||||
]
|
||||
if params.market_authority:
|
||||
touched_keys.append(
|
||||
AccountMeta(pubkey=params.market_authority, is_signer=False, is_writable=False),
|
||||
touched_accounts.append(
|
||||
AccountMeta(
|
||||
pubkey=params.market_authority, is_signer=False, is_writable=False
|
||||
),
|
||||
)
|
||||
return TransactionInstruction(
|
||||
keys=touched_keys,
|
||||
return Instruction(
|
||||
accounts=touched_accounts,
|
||||
program_id=params.program_id,
|
||||
data=INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.INIT_OPEN_ORDERS, args=dict())),
|
||||
data=INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.INIT_OPEN_ORDERS, args=dict())
|
||||
),
|
||||
)
|
||||
|
|
|
@ -3,8 +3,7 @@ from enum import IntEnum
|
|||
from typing import List, Optional, Tuple, Union, cast
|
||||
|
||||
from construct import Container
|
||||
from solana.publickey import PublicKey
|
||||
|
||||
from solders.pubkey import Pubkey
|
||||
from ..._layouts.queue import EVENT_LAYOUT, QUEUE_HEADER_LAYOUT, REQUEST_LAYOUT
|
||||
from ..types import Event, EventFlags, Request, ReuqestFlags
|
||||
|
||||
|
@ -18,19 +17,27 @@ def __from_bytes(
|
|||
buffer: bytes, queue_type: QueueType, history: Optional[int]
|
||||
) -> Tuple[Container, List[Union[Event, Request]]]:
|
||||
header = QUEUE_HEADER_LAYOUT.parse(buffer)
|
||||
layout_size = EVENT_LAYOUT.sizeof() if queue_type == QueueType.EVENT else REQUEST_LAYOUT.sizeof()
|
||||
layout_size = (
|
||||
EVENT_LAYOUT.sizeof()
|
||||
if queue_type == QueueType.EVENT
|
||||
else REQUEST_LAYOUT.sizeof()
|
||||
)
|
||||
alloc_len = math.floor((len(buffer) - QUEUE_HEADER_LAYOUT.sizeof()) / layout_size)
|
||||
nodes: List[Union[Event, Request]] = []
|
||||
if history:
|
||||
for i in range(min(history, alloc_len)):
|
||||
node_index = (header.head + header.count + alloc_len - 1 - i) % alloc_len
|
||||
offset = QUEUE_HEADER_LAYOUT.sizeof() + node_index * layout_size
|
||||
nodes.append(__parse_queue_item(buffer[offset : offset + layout_size], queue_type)) # noqa: E203
|
||||
nodes.append(
|
||||
__parse_queue_item(buffer[offset : offset + layout_size], queue_type)
|
||||
) # noqa: E203
|
||||
else:
|
||||
for i in range(header.count):
|
||||
node_index = (header.head + i) % alloc_len
|
||||
offset = QUEUE_HEADER_LAYOUT.sizeof() + node_index * layout_size
|
||||
nodes.append(__parse_queue_item(buffer[offset : offset + layout_size], queue_type)) # noqa: E203
|
||||
nodes.append(
|
||||
__parse_queue_item(buffer[offset : offset + layout_size], queue_type)
|
||||
) # noqa: E203
|
||||
return header, nodes
|
||||
|
||||
|
||||
|
@ -53,7 +60,7 @@ def __parse_queue_item(buffer: bytes, queue_type: QueueType) -> Union[Event, Req
|
|||
native_quantity_paid=parsed_item.native_quantity_paid,
|
||||
native_fee_or_rebate=parsed_item.native_fee_or_rebate,
|
||||
order_id=int.from_bytes(parsed_item.order_id, "little"),
|
||||
public_key=PublicKey(parsed_item.public_key),
|
||||
public_key=Pubkey.from_bytes(parsed_item.public_key),
|
||||
client_order_id=parsed_item.client_order_id,
|
||||
)
|
||||
else:
|
||||
|
@ -74,7 +81,7 @@ def __parse_queue_item(buffer: bytes, queue_type: QueueType) -> Union[Event, Req
|
|||
max_base_size_or_cancel_id=parsed_item.max_base_size_or_cancel_id,
|
||||
native_quote_quantity_locked=parsed_item.native_quote_quantity_locked,
|
||||
order_id=int.from_bytes(parsed_item.order_id, "little"),
|
||||
open_orders=PublicKey(parsed_item.open_orders),
|
||||
open_orders=Pubkey.from_bytes(parsed_item.open_orders),
|
||||
client_order_id=parsed_item.client_order_id,
|
||||
)
|
||||
|
||||
|
@ -82,12 +89,16 @@ def __parse_queue_item(buffer: bytes, queue_type: QueueType) -> Union[Event, Req
|
|||
def decode_request_queue(buffer: bytes, history: Optional[int] = None) -> List[Request]:
|
||||
header, nodes = __from_bytes(buffer, QueueType.REQUEST, history)
|
||||
if not header.account_flags.initialized or not header.account_flags.request_queue:
|
||||
raise Exception("Invalid requests queue, either not initialized or not a request queue.")
|
||||
raise Exception(
|
||||
"Invalid requests queue, either not initialized or not a request queue."
|
||||
)
|
||||
return cast(List[Request], nodes)
|
||||
|
||||
|
||||
def decode_event_queue(buffer: bytes, history: Optional[int] = None) -> List[Event]:
|
||||
header, nodes = __from_bytes(buffer, QueueType.EVENT, history)
|
||||
if not header.account_flags.initialized or not header.account_flags.event_queue:
|
||||
raise Exception("Invalid events queue, either not initialized or not a event queue.")
|
||||
raise Exception(
|
||||
"Invalid events queue, either not initialized or not a event queue."
|
||||
)
|
||||
return cast(List[Event], nodes)
|
||||
|
|
|
@ -4,8 +4,7 @@ from dataclasses import dataclass
|
|||
from typing import Iterable, List, NamedTuple, Optional
|
||||
|
||||
from construct import ListContainer
|
||||
from solana.publickey import PublicKey
|
||||
|
||||
from solders.pubkey import Pubkey
|
||||
from ..._layouts.slab import SLAB_LAYOUT, NodeType
|
||||
|
||||
|
||||
|
@ -33,7 +32,7 @@ class SlabLeafNode(SlabNode):
|
|||
owner_slot: int
|
||||
fee_tier: int
|
||||
key: int
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
quantity: int
|
||||
client_order_id: int
|
||||
|
||||
|
@ -64,7 +63,7 @@ class Slab:
|
|||
owner_slot=node.owner_slot,
|
||||
fee_tier=node.fee_tier,
|
||||
key=int.from_bytes(node.key, "little"),
|
||||
owner=PublicKey(node.owner),
|
||||
owner=Pubkey.from_bytes(node.owner),
|
||||
quantity=node.quantity,
|
||||
client_order_id=node.client_order_id,
|
||||
is_initialized=True,
|
||||
|
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
|
||||
from typing import List
|
||||
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
from solana.rpc.types import TxOpts
|
||||
from solana.transaction import Transaction
|
||||
|
@ -35,7 +35,9 @@ class AsyncMarket(MarketCore):
|
|||
market_state: MarketState,
|
||||
force_use_request_queue: bool = False,
|
||||
) -> None:
|
||||
super().__init__(market_state=market_state, force_use_request_queue=force_use_request_queue)
|
||||
super().__init__(
|
||||
market_state=market_state, force_use_request_queue=force_use_request_queue
|
||||
)
|
||||
self._conn = conn
|
||||
|
||||
@classmethod
|
||||
|
@ -43,8 +45,8 @@ class AsyncMarket(MarketCore):
|
|||
async def load(
|
||||
cls,
|
||||
conn: AsyncClient,
|
||||
market_address: PublicKey,
|
||||
program_id: PublicKey = instructions.DEFAULT_DEX_PROGRAM_ID,
|
||||
market_address: Pubkey,
|
||||
program_id: Pubkey = instructions.DEFAULT_DEX_PROGRAM_ID,
|
||||
force_use_request_queue: bool = False,
|
||||
) -> AsyncMarket:
|
||||
"""Factory method to create a Market.
|
||||
|
@ -56,7 +58,9 @@ class AsyncMarket(MarketCore):
|
|||
market_state = await MarketState.async_load(conn, market_address, program_id)
|
||||
return cls(conn, market_state, force_use_request_queue)
|
||||
|
||||
async def find_open_orders_accounts_for_owner(self, owner_address: PublicKey) -> List[AsyncOpenOrdersAccount]:
|
||||
async def find_open_orders_accounts_for_owner(
|
||||
self, owner_address: Pubkey
|
||||
) -> List[AsyncOpenOrdersAccount]:
|
||||
return await AsyncOpenOrdersAccount.find_for_market_and_owner(
|
||||
self._conn, self.state.public_key(), owner_address, self.state.program_id()
|
||||
)
|
||||
|
@ -71,11 +75,13 @@ class AsyncMarket(MarketCore):
|
|||
bytes_data = await load_bytes_data(self.state.asks(), self._conn)
|
||||
return self._parse_bids_or_asks(bytes_data)
|
||||
|
||||
async def load_orders_for_owner(self, owner_address: PublicKey) -> List[t.Order]:
|
||||
async def load_orders_for_owner(self, owner_address: Pubkey) -> List[t.Order]:
|
||||
"""Load orders for owner."""
|
||||
bids = await self.load_bids()
|
||||
asks = await self.load_asks()
|
||||
open_orders_accounts = await self.find_open_orders_accounts_for_owner(owner_address)
|
||||
open_orders_accounts = await self.find_open_orders_accounts_for_owner(
|
||||
owner_address
|
||||
)
|
||||
return self._parse_orders_for_owner(bids, asks, open_orders_accounts)
|
||||
|
||||
async def load_event_queue(self) -> List[t.Event]:
|
||||
|
@ -96,7 +102,7 @@ class AsyncMarket(MarketCore):
|
|||
|
||||
async def place_order( # pylint: disable=too-many-arguments,too-many-locals
|
||||
self,
|
||||
payer: PublicKey,
|
||||
payer: Pubkey,
|
||||
owner: Keypair,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
|
@ -107,11 +113,15 @@ class AsyncMarket(MarketCore):
|
|||
) -> SendTransactionResp: # TODO: Add open_orders_address_key param and fee_discount_pubkey
|
||||
transaction = Transaction()
|
||||
signers: List[Keypair] = [owner]
|
||||
open_order_accounts = await self.find_open_orders_accounts_for_owner(owner.public_key)
|
||||
open_order_accounts = await self.find_open_orders_accounts_for_owner(
|
||||
owner.pubkey()
|
||||
)
|
||||
if open_order_accounts:
|
||||
place_order_open_order_account = open_order_accounts[0].address
|
||||
else:
|
||||
mbfre_resp = await self._conn.get_minimum_balance_for_rent_exemption(OPEN_ORDERS_LAYOUT.sizeof())
|
||||
mbfre_resp = await self._conn.get_minimum_balance_for_rent_exemption(
|
||||
OPEN_ORDERS_LAYOUT.sizeof()
|
||||
)
|
||||
place_order_open_order_account = self._after_oo_mbfre_resp(
|
||||
mbfre_resp=mbfre_resp,
|
||||
owner=owner,
|
||||
|
@ -139,7 +149,7 @@ class AsyncMarket(MarketCore):
|
|||
async def cancel_order_by_client_id(
|
||||
self,
|
||||
owner: Keypair,
|
||||
open_orders_account: PublicKey,
|
||||
open_orders_account: Pubkey,
|
||||
client_id: int,
|
||||
opts: TxOpts = TxOpts(),
|
||||
) -> SendTransactionResp:
|
||||
|
@ -148,11 +158,15 @@ class AsyncMarket(MarketCore):
|
|||
)
|
||||
return await self._conn.send_transaction(txs, owner, opts=opts)
|
||||
|
||||
async def cancel_order(self, owner: Keypair, order: t.Order, opts: TxOpts = TxOpts()) -> SendTransactionResp:
|
||||
async def cancel_order(
|
||||
self, owner: Keypair, order: t.Order, opts: TxOpts = TxOpts()
|
||||
) -> SendTransactionResp:
|
||||
txn = self._build_cancel_order_tx(owner=owner, order=order)
|
||||
return await self._conn.send_transaction(txn, owner, opts=opts)
|
||||
|
||||
async def match_orders(self, fee_payer: Keypair, limit: int, opts: TxOpts = TxOpts()) -> SendTransactionResp:
|
||||
async def match_orders(
|
||||
self, fee_payer: Keypair, limit: int, opts: TxOpts = TxOpts()
|
||||
) -> SendTransactionResp:
|
||||
txn = self._build_match_orders_tx(limit)
|
||||
return await self._conn.send_transaction(txn, fee_payer, opts=opts)
|
||||
|
||||
|
@ -160,8 +174,8 @@ class AsyncMarket(MarketCore):
|
|||
self,
|
||||
owner: Keypair,
|
||||
open_orders: AsyncOpenOrdersAccount,
|
||||
base_wallet: PublicKey,
|
||||
quote_wallet: PublicKey, # TODO: add referrer_quote_wallet.
|
||||
base_wallet: Pubkey,
|
||||
quote_wallet: Pubkey, # TODO: add referrer_quote_wallet.
|
||||
opts: TxOpts = TxOpts(),
|
||||
) -> SendTransactionResp:
|
||||
# TODO: Handle wrapped sol accounts
|
||||
|
|
|
@ -5,10 +5,11 @@ import itertools
|
|||
import logging
|
||||
from typing import List, Union
|
||||
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solana.system_program import CreateAccountParams, create_account
|
||||
from solana.transaction import Transaction, TransactionInstruction
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solders.system_program import CreateAccountParams, create_account
|
||||
from solana.transaction import Transaction
|
||||
from solders.instruction import Instruction
|
||||
from solders.rpc.responses import GetMinimumBalanceForRentExemptionResp
|
||||
from spl.token.constants import ACCOUNT_LEN, TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT
|
||||
from spl.token.instructions import (
|
||||
|
@ -37,33 +38,40 @@ class MarketCore:
|
|||
|
||||
logger = logging.getLogger("pyserum.market.Market")
|
||||
|
||||
def __init__(self, market_state: MarketState, force_use_request_queue: bool = False) -> None:
|
||||
def __init__(
|
||||
self, market_state: MarketState, force_use_request_queue: bool = False
|
||||
) -> None:
|
||||
self.state = market_state
|
||||
self.force_use_request_queue = force_use_request_queue
|
||||
|
||||
def _use_request_queue(self) -> bool:
|
||||
return (
|
||||
# DEX Version 1
|
||||
self.state.program_id == PublicKey("4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn")
|
||||
self.state.program_id
|
||||
== Pubkey.from_string("4ckmDgGdxQoPDLUkDT3vHgSAkzA3QRdNq5ywwY4sUSJn")
|
||||
or
|
||||
# DEX Version 1
|
||||
self.state.program_id == PublicKey("BJ3jrUzddfuSrZHXSCxMUUQsjKEyLmuuyZebkcaFp2fg")
|
||||
self.state.program_id
|
||||
== Pubkey.from_string("BJ3jrUzddfuSrZHXSCxMUUQsjKEyLmuuyZebkcaFp2fg")
|
||||
or
|
||||
# DEX Version 2
|
||||
self.state.program_id == PublicKey("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o")
|
||||
self.state.program_id
|
||||
== Pubkey.from_string("EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o")
|
||||
or self.force_use_request_queue
|
||||
)
|
||||
|
||||
def support_srm_fee_discounts(self) -> bool:
|
||||
raise NotImplementedError("support_srm_fee_discounts not implemented")
|
||||
|
||||
def find_fee_discount_keys(self, owner: PublicKey, cache_duration: int):
|
||||
def find_fee_discount_keys(self, owner: Pubkey, cache_duration: int):
|
||||
raise NotImplementedError("find_fee_discount_keys not implemented")
|
||||
|
||||
def find_best_fee_discount_key(self, owner: PublicKey, cache_duration: int):
|
||||
def find_best_fee_discount_key(self, owner: Pubkey, cache_duration: int):
|
||||
raise NotImplementedError("find_best_fee_discount_key not implemented")
|
||||
|
||||
def find_quote_token_accounts_for_owner(self, owner_address: PublicKey, include_unwrapped_sol: bool = False):
|
||||
def find_quote_token_accounts_for_owner(
|
||||
self, owner_address: Pubkey, include_unwrapped_sol: bool = False
|
||||
):
|
||||
raise NotImplementedError("find_quote_token_accounts_for_owner not implemented")
|
||||
|
||||
def _parse_bids_or_asks(self, bytes_data: bytes) -> OrderBook:
|
||||
|
@ -76,7 +84,9 @@ class MarketCore:
|
|||
|
||||
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]
|
||||
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):
|
||||
|
@ -115,7 +125,8 @@ class MarketCore:
|
|||
side=side,
|
||||
price=price,
|
||||
size=size,
|
||||
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 _prepare_new_oo_account(
|
||||
|
@ -124,14 +135,14 @@ class MarketCore:
|
|||
balance_needed: int,
|
||||
signers: List[Keypair],
|
||||
transaction: Transaction,
|
||||
) -> PublicKey:
|
||||
) -> Pubkey:
|
||||
# new_open_orders_account = Account()
|
||||
new_open_orders_account = Keypair()
|
||||
place_order_open_order_account = new_open_orders_account.public_key
|
||||
place_order_open_order_account = new_open_orders_account.pubkey()
|
||||
transaction.add(
|
||||
make_create_account_instruction(
|
||||
owner_address=owner.public_key,
|
||||
new_account_address=new_open_orders_account.public_key,
|
||||
owner_address=owner.pubkey(),
|
||||
new_account_address=new_open_orders_account.pubkey(),
|
||||
lamports=balance_needed,
|
||||
program_id=self.state.program_id(),
|
||||
)
|
||||
|
@ -142,7 +153,7 @@ class MarketCore:
|
|||
def _prepare_order_transaction( # pylint: disable=too-many-arguments,too-many-locals
|
||||
self,
|
||||
transaction: Transaction,
|
||||
payer: PublicKey,
|
||||
payer: Pubkey,
|
||||
owner: Keypair,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
|
@ -150,42 +161,44 @@ class MarketCore:
|
|||
limit_price: float,
|
||||
max_quantity: float,
|
||||
client_id: int,
|
||||
open_order_accounts: Union[List[OpenOrdersAccount], List[AsyncOpenOrdersAccount]],
|
||||
place_order_open_order_account: PublicKey,
|
||||
open_order_accounts: Union[
|
||||
List[OpenOrdersAccount], List[AsyncOpenOrdersAccount]
|
||||
],
|
||||
place_order_open_order_account: Pubkey,
|
||||
) -> None:
|
||||
# unwrapped SOL cannot be used for payment
|
||||
if payer == owner.public_key:
|
||||
if payer == owner.pubkey():
|
||||
raise ValueError("Invalid payer account. Cannot use unwrapped SOL.")
|
||||
|
||||
# TODO: add integration test for SOL wrapping.
|
||||
should_wrap_sol = (side == Side.BUY and self.state.quote_mint() == WRAPPED_SOL_MINT) or (
|
||||
side == Side.SELL and self.state.base_mint() == WRAPPED_SOL_MINT
|
||||
)
|
||||
should_wrap_sol = (
|
||||
side == Side.BUY and self.state.quote_mint() == WRAPPED_SOL_MINT
|
||||
) or (side == Side.SELL and self.state.base_mint() == WRAPPED_SOL_MINT)
|
||||
|
||||
if should_wrap_sol:
|
||||
# wrapped_sol_account = Account()
|
||||
wrapped_sol_account = Keypair()
|
||||
payer = wrapped_sol_account.public_key
|
||||
payer = wrapped_sol_account.pubkey()
|
||||
signers.append(wrapped_sol_account)
|
||||
transaction.add(
|
||||
create_account(
|
||||
CreateAccountParams(
|
||||
from_pubkey=owner.public_key,
|
||||
new_account_pubkey=wrapped_sol_account.public_key,
|
||||
from_pubkey=owner.pubkey(),
|
||||
to_pubkey=wrapped_sol_account.pubkey(),
|
||||
lamports=self._get_lamport_need_for_sol_wrapping(
|
||||
limit_price, max_quantity, side, open_order_accounts
|
||||
),
|
||||
space=ACCOUNT_LEN,
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
owner=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
)
|
||||
transaction.add(
|
||||
initialize_account(
|
||||
InitializeAccountParams(
|
||||
account=wrapped_sol_account.public_key,
|
||||
account=wrapped_sol_account.pubkey(),
|
||||
mint=WRAPPED_SOL_MINT,
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
|
@ -208,9 +221,9 @@ class MarketCore:
|
|||
transaction.add(
|
||||
close_account(
|
||||
CloseAccountParams(
|
||||
account=wrapped_sol_account.public_key,
|
||||
owner=owner.public_key,
|
||||
dest=owner.public_key,
|
||||
account=wrapped_sol_account.pubkey(),
|
||||
owner=owner.pubkey(),
|
||||
dest=owner.pubkey(),
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
|
@ -222,9 +235,11 @@ class MarketCore:
|
|||
owner: Keypair,
|
||||
signers: List[Keypair],
|
||||
transaction: Transaction,
|
||||
) -> PublicKey:
|
||||
) -> Pubkey:
|
||||
balance_needed = mbfre_resp.value
|
||||
place_order_open_order_account = self._prepare_new_oo_account(owner, balance_needed, signers, transaction)
|
||||
place_order_open_order_account = self._prepare_new_oo_account(
|
||||
owner, balance_needed, signers, transaction
|
||||
)
|
||||
return place_order_open_order_account
|
||||
|
||||
@staticmethod
|
||||
|
@ -232,7 +247,9 @@ class MarketCore:
|
|||
price: float,
|
||||
size: float,
|
||||
side: Side,
|
||||
open_orders_accounts: Union[List[OpenOrdersAccount], List[AsyncOpenOrdersAccount]],
|
||||
open_orders_accounts: Union[
|
||||
List[OpenOrdersAccount], List[AsyncOpenOrdersAccount]
|
||||
],
|
||||
) -> int:
|
||||
lamports = 0
|
||||
if side == Side.BUY:
|
||||
|
@ -248,16 +265,16 @@ class MarketCore:
|
|||
|
||||
def make_place_order_instruction( # pylint: disable=too-many-arguments
|
||||
self,
|
||||
payer: PublicKey,
|
||||
payer: Pubkey,
|
||||
owner: Keypair,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
limit_price: float,
|
||||
max_quantity: float,
|
||||
client_id: int,
|
||||
open_order_account: PublicKey,
|
||||
fee_discount_pubkey: PublicKey = None,
|
||||
) -> TransactionInstruction:
|
||||
open_order_account: Pubkey,
|
||||
fee_discount_pubkey: Pubkey = None,
|
||||
) -> Instruction:
|
||||
if self.state.base_size_number_to_lots(max_quantity) < 0:
|
||||
raise Exception(f"Size lot %d is too small {max_quantity}")
|
||||
if self.state.price_number_to_lots(limit_price) < 0:
|
||||
|
@ -268,7 +285,7 @@ class MarketCore:
|
|||
market=self.state.public_key(),
|
||||
open_orders=open_order_account,
|
||||
payer=payer,
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
request_queue=self.state.request_queue(),
|
||||
base_vault=self.state.base_vault(),
|
||||
quote_vault=self.state.quote_vault(),
|
||||
|
@ -285,7 +302,7 @@ class MarketCore:
|
|||
market=self.state.public_key(),
|
||||
open_orders=open_order_account,
|
||||
payer=payer,
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
request_queue=self.state.request_queue(),
|
||||
event_queue=self.state.event_queue(),
|
||||
bids=self.state.bids(),
|
||||
|
@ -308,18 +325,22 @@ class MarketCore:
|
|||
)
|
||||
|
||||
def _build_cancel_order_by_client_id_tx(
|
||||
self, owner: Keypair, open_orders_account: PublicKey, client_id: int
|
||||
self, owner: Keypair, open_orders_account: Pubkey, client_id: int
|
||||
) -> Transaction:
|
||||
return Transaction().add(self.make_cancel_order_by_client_id_instruction(owner, open_orders_account, client_id))
|
||||
return Transaction().add(
|
||||
self.make_cancel_order_by_client_id_instruction(
|
||||
owner, open_orders_account, client_id
|
||||
)
|
||||
)
|
||||
|
||||
def make_cancel_order_by_client_id_instruction(
|
||||
self, owner: Keypair, open_orders_account: PublicKey, client_id: int
|
||||
) -> TransactionInstruction:
|
||||
self, owner: Keypair, open_orders_account: Pubkey, client_id: int
|
||||
) -> Instruction:
|
||||
if self._use_request_queue():
|
||||
return instructions.cancel_order_by_client_id(
|
||||
instructions.CancelOrderByClientIDParams(
|
||||
market=self.state.public_key(),
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
open_orders=open_orders_account,
|
||||
request_queue=self.state.request_queue(),
|
||||
client_id=client_id,
|
||||
|
@ -329,7 +350,7 @@ class MarketCore:
|
|||
return instructions.cancel_order_by_client_id_v2(
|
||||
instructions.CancelOrderByClientIDV2Params(
|
||||
market=self.state.public_key(),
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
open_orders=open_orders_account,
|
||||
bids=self.state.bids(),
|
||||
asks=self.state.asks(),
|
||||
|
@ -340,9 +361,13 @@ class MarketCore:
|
|||
)
|
||||
|
||||
def _build_cancel_order_tx(self, owner: Keypair, order: t.Order) -> Transaction:
|
||||
return Transaction().add(self.make_cancel_order_instruction(owner.public_key, order))
|
||||
return Transaction().add(
|
||||
self.make_cancel_order_instruction(owner.pubkey(), order)
|
||||
)
|
||||
|
||||
def make_cancel_order_instruction(self, owner: PublicKey, order: t.Order) -> TransactionInstruction:
|
||||
def make_cancel_order_instruction(
|
||||
self, owner: Pubkey, order: t.Order
|
||||
) -> Instruction:
|
||||
if self._use_request_queue():
|
||||
return instructions.cancel_order(
|
||||
instructions.CancelOrderParams(
|
||||
|
@ -374,7 +399,7 @@ class MarketCore:
|
|||
def _build_match_orders_tx(self, limit: int) -> Transaction:
|
||||
return Transaction().add(self.make_match_orders_instruction(limit))
|
||||
|
||||
def make_match_orders_instruction(self, limit: int) -> TransactionInstruction:
|
||||
def make_match_orders_instruction(self, limit: int) -> Instruction:
|
||||
params = instructions.MatchOrdersParams(
|
||||
market=self.state.public_key(),
|
||||
request_queue=self.state.request_queue(),
|
||||
|
@ -393,15 +418,15 @@ class MarketCore:
|
|||
owner: Keypair,
|
||||
signers: List[Keypair],
|
||||
open_orders: Union[OpenOrdersAccount, AsyncOpenOrdersAccount],
|
||||
base_wallet: PublicKey,
|
||||
quote_wallet: PublicKey, # TODO: add referrer_quote_wallet.
|
||||
base_wallet: Pubkey,
|
||||
quote_wallet: Pubkey, # TODO: add referrer_quote_wallet.
|
||||
min_bal_for_rent_exemption: int,
|
||||
should_wrap_sol: bool,
|
||||
) -> Transaction:
|
||||
# TODO: Handle wrapped sol accounts
|
||||
if open_orders.owner != owner.public_key:
|
||||
if open_orders.owner != owner.pubkey():
|
||||
raise Exception("Invalid open orders account")
|
||||
vault_signer = PublicKey.create_program_address(
|
||||
vault_signer = Pubkey.create_program_address(
|
||||
[
|
||||
bytes(self.state.public_key()),
|
||||
self.state.vault_signer_nonce().to_bytes(8, byteorder="little"),
|
||||
|
@ -418,11 +443,11 @@ class MarketCore:
|
|||
transaction.add(
|
||||
create_account(
|
||||
CreateAccountParams(
|
||||
from_pubkey=owner.public_key,
|
||||
new_account_pubkey=wrapped_sol_account.public_key,
|
||||
from_pubkey=owner.pubkey(),
|
||||
to_pubkey=wrapped_sol_account.pubkey(),
|
||||
lamports=min_bal_for_rent_exemption,
|
||||
space=ACCOUNT_LEN,
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
owner=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -430,9 +455,9 @@ class MarketCore:
|
|||
transaction.add(
|
||||
initialize_account(
|
||||
InitializeAccountParams(
|
||||
account=wrapped_sol_account.public_key,
|
||||
account=wrapped_sol_account.pubkey(),
|
||||
mint=WRAPPED_SOL_MINT,
|
||||
owner=owner.public_key,
|
||||
owner=owner.pubkey(),
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
|
@ -441,8 +466,12 @@ class MarketCore:
|
|||
transaction.add(
|
||||
self.make_settle_funds_instruction(
|
||||
open_orders,
|
||||
base_wallet if self.state.base_mint() != WRAPPED_SOL_MINT else wrapped_sol_account.public_key,
|
||||
quote_wallet if self.state.quote_mint() != WRAPPED_SOL_MINT else wrapped_sol_account.public_key,
|
||||
base_wallet
|
||||
if self.state.base_mint() != WRAPPED_SOL_MINT
|
||||
else wrapped_sol_account.pubkey(),
|
||||
quote_wallet
|
||||
if self.state.quote_mint() != WRAPPED_SOL_MINT
|
||||
else wrapped_sol_account.pubkey(),
|
||||
vault_signer,
|
||||
)
|
||||
)
|
||||
|
@ -452,9 +481,9 @@ class MarketCore:
|
|||
transaction.add(
|
||||
close_account(
|
||||
CloseAccountParams(
|
||||
account=wrapped_sol_account.public_key,
|
||||
owner=owner.public_key,
|
||||
dest=owner.public_key,
|
||||
account=wrapped_sol_account.pubkey(),
|
||||
owner=owner.pubkey(),
|
||||
dest=owner.pubkey(),
|
||||
program_id=TOKEN_PROGRAM_ID,
|
||||
)
|
||||
)
|
||||
|
@ -462,15 +491,17 @@ class MarketCore:
|
|||
return transaction
|
||||
|
||||
def _settle_funds_should_wrap_sol(self) -> bool:
|
||||
return (self.state.quote_mint() == WRAPPED_SOL_MINT) or (self.state.base_mint() == WRAPPED_SOL_MINT)
|
||||
return (self.state.quote_mint() == WRAPPED_SOL_MINT) or (
|
||||
self.state.base_mint() == WRAPPED_SOL_MINT
|
||||
)
|
||||
|
||||
def make_settle_funds_instruction(
|
||||
self,
|
||||
open_orders_account: Union[OpenOrdersAccount, AsyncOpenOrdersAccount],
|
||||
base_wallet: PublicKey,
|
||||
quote_wallet: PublicKey,
|
||||
vault_signer: PublicKey,
|
||||
) -> TransactionInstruction:
|
||||
base_wallet: Pubkey,
|
||||
quote_wallet: Pubkey,
|
||||
vault_signer: Pubkey,
|
||||
) -> Instruction:
|
||||
if base_wallet == self.state.base_vault():
|
||||
raise ValueError("base_wallet should not be a vault address")
|
||||
if quote_wallet == self.state.quote_vault():
|
||||
|
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
|
||||
from typing import List
|
||||
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.types import TxOpts
|
||||
from solana.transaction import Transaction
|
||||
|
@ -35,7 +35,9 @@ class Market(MarketCore):
|
|||
market_state: MarketState,
|
||||
force_use_request_queue: bool = False,
|
||||
) -> None:
|
||||
super().__init__(market_state=market_state, force_use_request_queue=force_use_request_queue)
|
||||
super().__init__(
|
||||
market_state=market_state, force_use_request_queue=force_use_request_queue
|
||||
)
|
||||
self._conn = conn
|
||||
|
||||
@classmethod
|
||||
|
@ -43,8 +45,8 @@ class Market(MarketCore):
|
|||
def load(
|
||||
cls,
|
||||
conn: Client,
|
||||
market_address: PublicKey,
|
||||
program_id: PublicKey = instructions.DEFAULT_DEX_PROGRAM_ID,
|
||||
market_address: Pubkey,
|
||||
program_id: Pubkey = instructions.DEFAULT_DEX_PROGRAM_ID,
|
||||
force_use_request_queue: bool = False,
|
||||
) -> Market:
|
||||
"""Factory method to create a Market.
|
||||
|
@ -56,7 +58,9 @@ class Market(MarketCore):
|
|||
market_state = MarketState.load(conn, market_address, program_id)
|
||||
return cls(conn, market_state, force_use_request_queue)
|
||||
|
||||
def find_open_orders_accounts_for_owner(self, owner_address: PublicKey) -> List[OpenOrdersAccount]:
|
||||
def find_open_orders_accounts_for_owner(
|
||||
self, owner_address: Pubkey
|
||||
) -> List[OpenOrdersAccount]:
|
||||
return OpenOrdersAccount.find_for_market_and_owner(
|
||||
self._conn, self.state.public_key(), owner_address, self.state.program_id()
|
||||
)
|
||||
|
@ -71,7 +75,7 @@ class Market(MarketCore):
|
|||
bytes_data = load_bytes_data(self.state.asks(), self._conn)
|
||||
return self._parse_bids_or_asks(bytes_data)
|
||||
|
||||
def load_orders_for_owner(self, owner_address: PublicKey) -> List[t.Order]:
|
||||
def load_orders_for_owner(self, owner_address: Pubkey) -> List[t.Order]:
|
||||
"""Load orders for owner."""
|
||||
bids = self.load_bids()
|
||||
asks = self.load_asks()
|
||||
|
@ -96,7 +100,7 @@ class Market(MarketCore):
|
|||
|
||||
def place_order( # pylint: disable=too-many-arguments,too-many-locals
|
||||
self,
|
||||
payer: PublicKey,
|
||||
payer: Pubkey,
|
||||
owner: Keypair,
|
||||
order_type: OrderType,
|
||||
side: Side,
|
||||
|
@ -107,11 +111,13 @@ class Market(MarketCore):
|
|||
) -> SendTransactionResp: # TODO: Add open_orders_address_key param and fee_discount_pubkey
|
||||
transaction = Transaction()
|
||||
signers: List[Keypair] = [owner]
|
||||
open_order_accounts = self.find_open_orders_accounts_for_owner(owner.public_key)
|
||||
open_order_accounts = self.find_open_orders_accounts_for_owner(owner.pubkey())
|
||||
if open_order_accounts:
|
||||
place_order_open_order_account = open_order_accounts[0].address
|
||||
else:
|
||||
mbfre_resp = self._conn.get_minimum_balance_for_rent_exemption(OPEN_ORDERS_LAYOUT.sizeof())
|
||||
mbfre_resp = self._conn.get_minimum_balance_for_rent_exemption(
|
||||
OPEN_ORDERS_LAYOUT.sizeof()
|
||||
)
|
||||
place_order_open_order_account = self._after_oo_mbfre_resp(
|
||||
mbfre_resp=mbfre_resp,
|
||||
owner=owner,
|
||||
|
@ -139,7 +145,7 @@ class Market(MarketCore):
|
|||
def cancel_order_by_client_id(
|
||||
self,
|
||||
owner: Keypair,
|
||||
open_orders_account: PublicKey,
|
||||
open_orders_account: Pubkey,
|
||||
client_id: int,
|
||||
opts: TxOpts = TxOpts(),
|
||||
) -> SendTransactionResp:
|
||||
|
@ -148,11 +154,15 @@ class Market(MarketCore):
|
|||
)
|
||||
return self._conn.send_transaction(txs, owner, opts=opts)
|
||||
|
||||
def cancel_order(self, owner: Keypair, order: t.Order, opts: TxOpts = TxOpts()) -> SendTransactionResp:
|
||||
def cancel_order(
|
||||
self, owner: Keypair, order: t.Order, opts: TxOpts = TxOpts()
|
||||
) -> SendTransactionResp:
|
||||
txn = self._build_cancel_order_tx(owner=owner, order=order)
|
||||
return self._conn.send_transaction(txn, owner, opts=opts)
|
||||
|
||||
def match_orders(self, fee_payer: Keypair, limit: int, opts: TxOpts = TxOpts()) -> SendTransactionResp:
|
||||
def match_orders(
|
||||
self, fee_payer: Keypair, limit: int, opts: TxOpts = TxOpts()
|
||||
) -> SendTransactionResp:
|
||||
txn = self._build_match_orders_tx(limit)
|
||||
return self._conn.send_transaction(txn, fee_payer, opts=opts)
|
||||
|
||||
|
@ -160,14 +170,16 @@ class Market(MarketCore):
|
|||
self,
|
||||
owner: Keypair,
|
||||
open_orders: OpenOrdersAccount,
|
||||
base_wallet: PublicKey,
|
||||
quote_wallet: PublicKey, # TODO: add referrer_quote_wallet.
|
||||
base_wallet: Pubkey,
|
||||
quote_wallet: Pubkey, # TODO: add referrer_quote_wallet.
|
||||
opts: TxOpts = TxOpts(),
|
||||
) -> SendTransactionResp:
|
||||
# TODO: Handle wrapped sol accounts
|
||||
should_wrap_sol = self._settle_funds_should_wrap_sol()
|
||||
min_bal_for_rent_exemption = (
|
||||
self._conn.get_minimum_balance_for_rent_exemption(165).value if should_wrap_sol else 0
|
||||
self._conn.get_minimum_balance_for_rent_exemption(165).value
|
||||
if should_wrap_sol
|
||||
else 0
|
||||
) # value only matters if should_wrap_sol
|
||||
signers = [owner]
|
||||
transaction = self._build_settle_funds_tx(
|
||||
|
|
|
@ -16,9 +16,13 @@ class OrderBook:
|
|||
_is_bids: bool
|
||||
_slab: Slab
|
||||
|
||||
def __init__(self, market_state: MarketState, account_flags: t.AccountFlags, slab: Slab) -> None:
|
||||
def __init__(
|
||||
self, market_state: MarketState, account_flags: t.AccountFlags, slab: Slab
|
||||
) -> None:
|
||||
if not account_flags.initialized or not account_flags.bids ^ account_flags.asks:
|
||||
raise Exception("Invalid order book, either not initialized or neither of bids or asks")
|
||||
raise Exception(
|
||||
"Invalid order book, either not initialized or neither of bids or asks"
|
||||
)
|
||||
self._market_state = market_state
|
||||
self._is_bids = account_flags.bids
|
||||
self._slab = slab
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import math
|
||||
|
||||
from construct import Container, Struct
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
|
||||
|
@ -15,7 +15,11 @@ from .types import AccountFlags
|
|||
|
||||
class MarketState: # pylint: disable=too-many-public-methods
|
||||
def __init__(
|
||||
self, parsed_market: Container, program_id: PublicKey, base_mint_decimals: int, quote_mint_decimals: int
|
||||
self,
|
||||
parsed_market: Container,
|
||||
program_id: Pubkey,
|
||||
base_mint_decimals: int,
|
||||
quote_mint_decimals: int,
|
||||
) -> None:
|
||||
self._decoded = parsed_market
|
||||
self._program_id = program_id
|
||||
|
@ -32,77 +36,99 @@ class MarketState: # pylint: disable=too-many-public-methods
|
|||
parsed_market = MARKET_LAYOUT.parse(bytes_data)
|
||||
# TODO: add ownAddress check!
|
||||
|
||||
if not parsed_market.account_flags.initialized or not parsed_market.account_flags.market:
|
||||
if (
|
||||
not parsed_market.account_flags.initialized
|
||||
or not parsed_market.account_flags.market
|
||||
):
|
||||
raise Exception("Invalid market")
|
||||
return parsed_market
|
||||
|
||||
@classmethod
|
||||
def load(cls, conn: Client, market_address: PublicKey, program_id: PublicKey) -> MarketState:
|
||||
def load(
|
||||
cls, conn: Client, market_address: Pubkey, program_id: Pubkey
|
||||
) -> MarketState:
|
||||
bytes_data = utils.load_bytes_data(market_address, conn)
|
||||
parsed_market = cls._make_parsed_market(bytes_data)
|
||||
|
||||
base_mint_decimals = utils.get_mint_decimals(conn, PublicKey(parsed_market.base_mint))
|
||||
quote_mint_decimals = utils.get_mint_decimals(conn, PublicKey(parsed_market.quote_mint))
|
||||
base_mint_decimals = utils.get_mint_decimals(
|
||||
conn, Pubkey.from_bytes(parsed_market.base_mint)
|
||||
)
|
||||
quote_mint_decimals = utils.get_mint_decimals(
|
||||
conn, Pubkey.from_bytes(parsed_market.quote_mint)
|
||||
)
|
||||
return cls(parsed_market, program_id, base_mint_decimals, quote_mint_decimals)
|
||||
|
||||
@classmethod
|
||||
async def async_load(cls, conn: AsyncClient, market_address: PublicKey, program_id: PublicKey) -> MarketState:
|
||||
async def async_load(
|
||||
cls, conn: AsyncClient, market_address: Pubkey, program_id: Pubkey
|
||||
) -> MarketState:
|
||||
bytes_data = await async_utils.load_bytes_data(market_address, conn)
|
||||
parsed_market = cls._make_parsed_market(bytes_data)
|
||||
base_mint_decimals = await async_utils.get_mint_decimals(conn, PublicKey(parsed_market.base_mint))
|
||||
quote_mint_decimals = await async_utils.get_mint_decimals(conn, PublicKey(parsed_market.quote_mint))
|
||||
base_mint_decimals = await async_utils.get_mint_decimals(
|
||||
conn, Pubkey.from_bytes(parsed_market.base_mint)
|
||||
)
|
||||
quote_mint_decimals = await async_utils.get_mint_decimals(
|
||||
conn, Pubkey.from_bytes(parsed_market.quote_mint)
|
||||
)
|
||||
return cls(parsed_market, program_id, base_mint_decimals, quote_mint_decimals)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(
|
||||
cls, program_id: PublicKey, base_mint_decimals: int, quote_mint_decimals: int, buffer: bytes
|
||||
cls,
|
||||
program_id: Pubkey,
|
||||
base_mint_decimals: int,
|
||||
quote_mint_decimals: int,
|
||||
buffer: bytes,
|
||||
) -> MarketState:
|
||||
parsed_market = MARKET_LAYOUT.parse(buffer)
|
||||
# TODO: add ownAddress check!
|
||||
|
||||
if not parsed_market.account_flags.initialized or not parsed_market.account_flags.market:
|
||||
if (
|
||||
not parsed_market.account_flags.initialized
|
||||
or not parsed_market.account_flags.market
|
||||
):
|
||||
raise Exception("Invalid market")
|
||||
|
||||
return cls(parsed_market, program_id, base_mint_decimals, quote_mint_decimals)
|
||||
|
||||
def program_id(self) -> PublicKey:
|
||||
def program_id(self) -> Pubkey:
|
||||
return self._program_id
|
||||
|
||||
def public_key(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.own_address)
|
||||
def public_key(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.own_address)
|
||||
|
||||
def account_flags(self) -> AccountFlags:
|
||||
return AccountFlags(**self._decoded.account_flags)
|
||||
|
||||
def asks(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.asks)
|
||||
def asks(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.asks)
|
||||
|
||||
def bids(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.bids)
|
||||
def bids(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.bids)
|
||||
|
||||
def fee_rate_bps(self) -> int:
|
||||
return self._decoded.fee_rate_bps
|
||||
|
||||
def event_queue(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.event_queue)
|
||||
def event_queue(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.event_queue)
|
||||
|
||||
def request_queue(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.request_queue)
|
||||
def request_queue(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.request_queue)
|
||||
|
||||
def vault_signer_nonce(self) -> int:
|
||||
return self._decoded.vault_signer_nonce
|
||||
|
||||
def base_mint(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.base_mint)
|
||||
def base_mint(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.base_mint)
|
||||
|
||||
def quote_mint(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.quote_mint)
|
||||
def quote_mint(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.quote_mint)
|
||||
|
||||
def base_vault(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.base_vault)
|
||||
def base_vault(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.base_vault)
|
||||
|
||||
def quote_vault(self) -> PublicKey:
|
||||
return PublicKey(self._decoded.quote_vault)
|
||||
def quote_vault(self) -> Pubkey:
|
||||
return Pubkey.from_bytes(self._decoded.quote_vault)
|
||||
|
||||
def base_deposits_total(self) -> int:
|
||||
return self._decoded.base_deposits_total
|
||||
|
@ -144,9 +170,9 @@ class MarketState: # pylint: disable=too-many-public-methods
|
|||
return self._decoded.quote_lot_size
|
||||
|
||||
def price_lots_to_number(self, price: int) -> float:
|
||||
return float(price * self.quote_lot_size() * self.base_spl_token_multiplier()) / (
|
||||
self.base_lot_size() * self.quote_spl_token_multiplier()
|
||||
)
|
||||
return float(
|
||||
price * self.quote_lot_size() * self.base_spl_token_multiplier()
|
||||
) / (self.base_lot_size() * self.quote_spl_token_multiplier())
|
||||
|
||||
def price_number_to_lots(self, price: float) -> int:
|
||||
return int(
|
||||
|
@ -160,10 +186,14 @@ class MarketState: # pylint: disable=too-many-public-methods
|
|||
return float(size * self.base_lot_size()) / self.base_spl_token_multiplier()
|
||||
|
||||
def base_size_number_to_lots(self, size: float) -> int:
|
||||
return int(math.floor(size * self.base_spl_token_multiplier()) / 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())
|
||||
return int(
|
||||
math.floor(size * self.quote_spl_token_multiplier()) / self.quote_lot_size()
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import NamedTuple
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
from .._layouts.account_flags import ACCOUNT_FLAGS_LAYOUT
|
||||
from ..enums import Side
|
||||
|
@ -67,7 +67,7 @@ class Order(NamedTuple):
|
|||
""""""
|
||||
client_id: int
|
||||
""""""
|
||||
open_order_address: PublicKey
|
||||
open_order_address: Pubkey
|
||||
""""""
|
||||
open_order_slot: int
|
||||
""""""
|
||||
|
@ -100,7 +100,7 @@ class Request(NamedTuple):
|
|||
""""""
|
||||
order_id: int
|
||||
""""""
|
||||
open_orders: PublicKey
|
||||
open_orders: Pubkey
|
||||
""""""
|
||||
client_order_id: int
|
||||
""""""
|
||||
|
@ -128,7 +128,7 @@ class Event(NamedTuple):
|
|||
""""""
|
||||
order_id: int
|
||||
""""""
|
||||
public_key: PublicKey
|
||||
public_key: Pubkey
|
||||
""""""
|
||||
client_order_id: int
|
||||
""""""
|
||||
|
@ -137,14 +137,14 @@ class Event(NamedTuple):
|
|||
class MarketInfo(NamedTuple):
|
||||
name: str
|
||||
""""""
|
||||
address: PublicKey
|
||||
address: Pubkey
|
||||
""""""
|
||||
program_id: PublicKey
|
||||
program_id: Pubkey
|
||||
""""""
|
||||
|
||||
|
||||
class TokenInfo(NamedTuple):
|
||||
name: str
|
||||
""""""
|
||||
address: PublicKey
|
||||
address: Pubkey
|
||||
""""""
|
||||
|
|
|
@ -2,12 +2,12 @@ from __future__ import annotations
|
|||
|
||||
from typing import List, NamedTuple, Tuple, Type, TypeVar
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.commitment import Processed
|
||||
from solana.rpc.types import Commitment, MemcmpOpts
|
||||
from solana.system_program import CreateAccountParams, create_account
|
||||
from solana.transaction import TransactionInstruction
|
||||
from solders.system_program import CreateAccountParams, create_account
|
||||
from solders.instruction import Instruction
|
||||
from solders.rpc.responses import GetProgramAccountsResp
|
||||
|
||||
from ._layouts.open_orders import OPEN_ORDERS_LAYOUT
|
||||
|
@ -16,11 +16,11 @@ from .utils import load_bytes_data
|
|||
|
||||
|
||||
class ProgramAccount(NamedTuple):
|
||||
public_key: PublicKey
|
||||
public_key: Pubkey
|
||||
data: bytes
|
||||
is_executablable: bool
|
||||
lamports: int
|
||||
owner: PublicKey
|
||||
owner: Pubkey
|
||||
|
||||
|
||||
_T = TypeVar("_T", bound="_OpenOrdersAccountCore")
|
||||
|
@ -30,9 +30,9 @@ class _OpenOrdersAccountCore: # pylint: disable=too-many-instance-attributes,to
|
|||
# pylint: disable=too-many-arguments
|
||||
def __init__(
|
||||
self,
|
||||
address: PublicKey,
|
||||
market: PublicKey,
|
||||
owner: PublicKey,
|
||||
address: Pubkey,
|
||||
market: Pubkey,
|
||||
owner: Pubkey,
|
||||
base_token_free: int,
|
||||
base_token_total: int,
|
||||
quote_token_free: int,
|
||||
|
@ -55,56 +55,67 @@ class _OpenOrdersAccountCore: # pylint: disable=too-many-instance-attributes,to
|
|||
self.client_ids = client_ids
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls: Type[_T], address: PublicKey, buffer: bytes) -> _T:
|
||||
def from_bytes(cls: Type[_T], address: Pubkey, buffer: bytes) -> _T:
|
||||
open_order_decoded = OPEN_ORDERS_LAYOUT.parse(buffer)
|
||||
if not open_order_decoded.account_flags.open_orders or not open_order_decoded.account_flags.initialized:
|
||||
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 cls(
|
||||
address=address,
|
||||
market=PublicKey(open_order_decoded.market),
|
||||
owner=PublicKey(open_order_decoded.owner),
|
||||
market=Pubkey.from_bytes(open_order_decoded.market),
|
||||
owner=Pubkey.from_bytes(open_order_decoded.owner),
|
||||
base_token_free=open_order_decoded.base_token_free,
|
||||
base_token_total=open_order_decoded.base_token_total,
|
||||
quote_token_free=open_order_decoded.quote_token_free,
|
||||
quote_token_total=open_order_decoded.quote_token_total,
|
||||
free_slot_bits=int.from_bytes(open_order_decoded.free_slot_bits, "little"),
|
||||
is_bid_bits=int.from_bytes(open_order_decoded.is_bid_bits, "little"),
|
||||
orders=[int.from_bytes(order, "little") for order in open_order_decoded.orders],
|
||||
orders=[
|
||||
int.from_bytes(order, "little") for order in open_order_decoded.orders
|
||||
],
|
||||
client_ids=open_order_decoded.client_ids,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _process_get_program_accounts_resp(cls: Type[_T], resp: GetProgramAccountsResp) -> List[_T]:
|
||||
def _process_get_program_accounts_resp(
|
||||
cls: Type[_T], resp: GetProgramAccountsResp
|
||||
) -> List[_T]:
|
||||
accounts = []
|
||||
for keyed_account in resp.value:
|
||||
account_details = keyed_account.account
|
||||
accounts.append(
|
||||
ProgramAccount(
|
||||
public_key=PublicKey(keyed_account.pubkey),
|
||||
public_key=keyed_account.pubkey,
|
||||
data=account_details.data,
|
||||
is_executablable=account_details.executable,
|
||||
owner=PublicKey(account_details.owner),
|
||||
owner=account_details.owner,
|
||||
lamports=account_details.lamports,
|
||||
)
|
||||
)
|
||||
|
||||
return [cls.from_bytes(account.public_key, account.data) for account in accounts]
|
||||
return [
|
||||
cls.from_bytes(account.public_key, account.data) for account in accounts
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _build_get_program_accounts_args(
|
||||
market: PublicKey,
|
||||
program_id: PublicKey,
|
||||
owner: PublicKey,
|
||||
market: Pubkey,
|
||||
program_id: Pubkey,
|
||||
owner: Pubkey,
|
||||
commitment: Commitment,
|
||||
) -> Tuple[PublicKey, Commitment, str, None, List[MemcmpOpts]]:
|
||||
) -> Tuple[Pubkey, Commitment, str, None, List[MemcmpOpts]]:
|
||||
filters = [
|
||||
MemcmpOpts(
|
||||
offset=5 + 8, # 5 bytes of padding, 8 bytes of account flag
|
||||
bytes=str(market),
|
||||
),
|
||||
MemcmpOpts(
|
||||
offset=5 + 8 + 32, # 5 bytes of padding, 8 bytes of account flag, 32 bytes of market public key
|
||||
offset=5
|
||||
+ 8
|
||||
+ 32, # 5 bytes of padding, 8 bytes of account flag, 32 bytes of market public key
|
||||
bytes=str(owner),
|
||||
),
|
||||
]
|
||||
|
@ -123,9 +134,9 @@ class OpenOrdersAccount(_OpenOrdersAccountCore):
|
|||
def find_for_market_and_owner( # pylint: disable=too-many-arguments
|
||||
cls,
|
||||
conn: Client,
|
||||
market: PublicKey,
|
||||
owner: PublicKey,
|
||||
program_id: PublicKey,
|
||||
market: Pubkey,
|
||||
owner: Pubkey,
|
||||
program_id: Pubkey,
|
||||
commitment: Commitment = Processed,
|
||||
) -> List[OpenOrdersAccount]:
|
||||
args = cls._build_get_program_accounts_args(
|
||||
|
@ -136,23 +147,23 @@ class OpenOrdersAccount(_OpenOrdersAccountCore):
|
|||
|
||||
@classmethod
|
||||
def load(cls, conn: Client, address: str) -> OpenOrdersAccount:
|
||||
addr_pub_key = PublicKey(address)
|
||||
addr_pub_key = Pubkey.from_string(address)
|
||||
bytes_data = load_bytes_data(addr_pub_key, conn)
|
||||
return cls.from_bytes(addr_pub_key, bytes_data)
|
||||
|
||||
|
||||
def make_create_account_instruction(
|
||||
owner_address: PublicKey,
|
||||
new_account_address: PublicKey,
|
||||
owner_address: Pubkey,
|
||||
new_account_address: Pubkey,
|
||||
lamports: int,
|
||||
program_id: PublicKey = DEFAULT_DEX_PROGRAM_ID,
|
||||
) -> TransactionInstruction:
|
||||
program_id: Pubkey = DEFAULT_DEX_PROGRAM_ID,
|
||||
) -> Instruction:
|
||||
return create_account(
|
||||
CreateAccountParams(
|
||||
from_pubkey=owner_address,
|
||||
new_account_pubkey=new_account_address,
|
||||
to_pubkey=new_account_address,
|
||||
lamports=lamports,
|
||||
space=OPEN_ORDERS_LAYOUT.sizeof(),
|
||||
program_id=program_id,
|
||||
owner=program_id,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solders.account import Account
|
||||
from solders.rpc.responses import GetAccountInfoResp
|
||||
|
@ -13,7 +13,7 @@ def parse_bytes_data(res: GetAccountInfoResp) -> bytes:
|
|||
return res.value.data
|
||||
|
||||
|
||||
def load_bytes_data(addr: PublicKey, conn: Client) -> bytes:
|
||||
def load_bytes_data(addr: Pubkey, conn: Client) -> bytes:
|
||||
res = conn.get_account_info(addr)
|
||||
return parse_bytes_data(res)
|
||||
|
||||
|
@ -22,7 +22,7 @@ def parse_mint_decimals(bytes_data: bytes) -> int:
|
|||
return MINT_LAYOUT.parse(bytes_data).decimals
|
||||
|
||||
|
||||
def get_mint_decimals(conn: Client, mint_pub_key: PublicKey) -> int:
|
||||
def get_mint_decimals(conn: Client, mint_pub_key: Pubkey) -> int:
|
||||
"""Get the mint decimals for a token mint"""
|
||||
if mint_pub_key == WRAPPED_SOL_MINT:
|
||||
return 9
|
||||
|
|
2
setup.py
2
setup.py
|
@ -4,7 +4,7 @@ from setuptools import find_packages, setup
|
|||
|
||||
setup(
|
||||
name="pyserum",
|
||||
version="0.6.0a",
|
||||
version="0.7.0a",
|
||||
author="serum-community",
|
||||
description="""Python client library for interacting with the Project Serum DEX.""",
|
||||
install_requires=[
|
||||
|
|
|
@ -2,8 +2,8 @@ import asyncio
|
|||
from typing import Dict
|
||||
|
||||
import pytest
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
|
||||
|
@ -29,16 +29,16 @@ def __bs_params() -> Dict[str, str]:
|
|||
def __bootstrap_account(pubkey: str, secretkey: str) -> Keypair:
|
||||
secret = [int(b) for b in secretkey[1:-1].split(" ")]
|
||||
secret_bytes = bytes(secret)
|
||||
keypair = Keypair.from_secret_key(secret_bytes)
|
||||
assert str(keypair.public_key) == pubkey, "account must map to provided public key"
|
||||
keypair = Keypair.from_bytes(secret_bytes)
|
||||
assert str(keypair.pubkey()) == pubkey, "account must map to provided public key"
|
||||
return keypair
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_dex_program_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_dex_program_pk(__bs_params) -> Pubkey:
|
||||
"""Bootstrapped dex program id."""
|
||||
return PublicKey(__bs_params["dex_program_id"])
|
||||
return Pubkey.from_string(__bs_params["dex_program_id"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -52,7 +52,9 @@ def stubbed_payer(__bs_params) -> Keypair:
|
|||
@pytest.fixture(scope="session")
|
||||
def stubbed_base_mint(__bs_params) -> Keypair:
|
||||
"""Bootstrapped base mint account."""
|
||||
return __bootstrap_account(__bs_params["coin_mint"], __bs_params["coin_mint_secret"])
|
||||
return __bootstrap_account(
|
||||
__bs_params["coin_mint"], __bs_params["coin_mint_secret"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -66,84 +68,88 @@ def stubbed_quote_mint(__bs_params) -> Keypair:
|
|||
@pytest.fixture(scope="session")
|
||||
def stubbed_base_wallet(__bs_params) -> Keypair:
|
||||
"""Bootstrapped base mint account."""
|
||||
return __bootstrap_account(__bs_params["coin_wallet"], __bs_params["coin_wallet_secret"])
|
||||
return __bootstrap_account(
|
||||
__bs_params["coin_wallet"], __bs_params["coin_wallet_secret"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_quote_wallet(__bs_params) -> Keypair:
|
||||
"""Bootstrapped quote mint account."""
|
||||
return __bootstrap_account(__bs_params["pc_wallet"], __bs_params["pc_wallet_secret"])
|
||||
return __bootstrap_account(
|
||||
__bs_params["pc_wallet"], __bs_params["pc_wallet_secret"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_market_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_market_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the boostrapped market."""
|
||||
return PublicKey(__bs_params["market"])
|
||||
return Pubkey.from_string(__bs_params["market"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_req_q_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_req_q_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the bootstrapped request queue."""
|
||||
return PublicKey(__bs_params["req_q"])
|
||||
return Pubkey.from_string(__bs_params["req_q"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_event_q_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_event_q_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the bootstrapped request queue."""
|
||||
return PublicKey(__bs_params["event_q"])
|
||||
return Pubkey.from_string(__bs_params["event_q"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_bids_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_bids_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the bootstrapped bids book."""
|
||||
return PublicKey(__bs_params["bids"])
|
||||
return Pubkey.from_string(__bs_params["bids"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_asks_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_asks_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the bootstrapped asks book."""
|
||||
return PublicKey(__bs_params["asks"])
|
||||
return Pubkey.from_string(__bs_params["asks"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_base_vault_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_base_vault_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the base vault account."""
|
||||
return PublicKey(__bs_params["coin_vault"])
|
||||
return Pubkey.from_string(__bs_params["coin_vault"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_quote_vault_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_quote_vault_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the quote vault account."""
|
||||
return PublicKey(__bs_params["pc_vault"])
|
||||
return Pubkey.from_string(__bs_params["pc_vault"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_vault_signer_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_vault_signer_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the bootstrapped vault signer."""
|
||||
return PublicKey(__bs_params["vault_signer_key"])
|
||||
return Pubkey.from_string(__bs_params["vault_signer_key"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_bid_account_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_bid_account_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the initial bid order account."""
|
||||
return PublicKey(__bs_params["bid_account"])
|
||||
return Pubkey.from_string(__bs_params["bid_account"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="session")
|
||||
def stubbed_ask_account_pk(__bs_params) -> PublicKey:
|
||||
def stubbed_ask_account_pk(__bs_params) -> Pubkey:
|
||||
"""Public key of the initial ask order account."""
|
||||
return PublicKey(__bs_params["ask_account"])
|
||||
return Pubkey.from_string(__bs_params["ask_account"])
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -152,7 +158,9 @@ def http_client() -> Client:
|
|||
"""Solana http client."""
|
||||
cc = conn("http://localhost:8899") # pylint: disable=invalid-name
|
||||
if not cc.is_connected():
|
||||
raise Exception("Could not connect to local node. Please run `make int-tests` to run integration tests.")
|
||||
raise Exception(
|
||||
"Could not connect to local node. Please run `make int-tests` to run integration tests."
|
||||
)
|
||||
return cc
|
||||
|
||||
|
||||
|
@ -166,7 +174,9 @@ def event_loop():
|
|||
|
||||
@pytest.mark.async_integration
|
||||
@pytest.fixture(scope="session")
|
||||
def async_http_client(event_loop) -> AsyncClient: # pylint: disable=redefined-outer-name
|
||||
def async_http_client(
|
||||
event_loop,
|
||||
) -> AsyncClient: # pylint: disable=redefined-outer-name
|
||||
"""Solana async http client."""
|
||||
cc = async_conn("http://localhost:8899") # pylint: disable=invalid-name
|
||||
if not event_loop.run_until_complete(cc.is_connected()):
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# pylint: disable=redefined-outer-name
|
||||
|
||||
import pytest
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
from solana.rpc.types import TxOpts
|
||||
|
||||
|
@ -13,10 +13,18 @@ from pyserum.market import AsyncMarket
|
|||
@pytest.mark.async_integration
|
||||
@pytest.fixture(scope="module")
|
||||
def bootstrapped_market(
|
||||
async_http_client: AsyncClient, stubbed_market_pk: PublicKey, stubbed_dex_program_pk: PublicKey, event_loop
|
||||
async_http_client: AsyncClient,
|
||||
stubbed_market_pk: Pubkey,
|
||||
stubbed_dex_program_pk: Pubkey,
|
||||
event_loop,
|
||||
) -> AsyncMarket:
|
||||
return event_loop.run_until_complete(
|
||||
AsyncMarket.load(async_http_client, stubbed_market_pk, stubbed_dex_program_pk, force_use_request_queue=True)
|
||||
AsyncMarket.load(
|
||||
async_http_client,
|
||||
stubbed_market_pk,
|
||||
stubbed_dex_program_pk,
|
||||
force_use_request_queue=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -24,16 +32,16 @@ def bootstrapped_market(
|
|||
@pytest.mark.asyncio
|
||||
async def test_bootstrapped_market(
|
||||
bootstrapped_market: AsyncMarket,
|
||||
stubbed_market_pk: PublicKey,
|
||||
stubbed_dex_program_pk: PublicKey,
|
||||
stubbed_market_pk: Pubkey,
|
||||
stubbed_dex_program_pk: Pubkey,
|
||||
stubbed_base_mint: Keypair,
|
||||
stubbed_quote_mint: Keypair,
|
||||
):
|
||||
assert isinstance(bootstrapped_market, AsyncMarket)
|
||||
assert bootstrapped_market.state.public_key() == stubbed_market_pk
|
||||
assert bootstrapped_market.state.pubkey()() == stubbed_market_pk
|
||||
assert bootstrapped_market.state.program_id() == stubbed_dex_program_pk
|
||||
assert bootstrapped_market.state.base_mint() == stubbed_base_mint.public_key
|
||||
assert bootstrapped_market.state.quote_mint() == stubbed_quote_mint.public_key
|
||||
assert bootstrapped_market.state.base_mint() == stubbed_base_mint.pubkey()
|
||||
assert bootstrapped_market.state.quote_mint() == stubbed_quote_mint.pubkey()
|
||||
|
||||
|
||||
@pytest.mark.async_integration
|
||||
|
@ -70,7 +78,9 @@ async def test_market_load_requests(bootstrapped_market: AsyncMarket):
|
|||
@pytest.mark.async_integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_match_order(bootstrapped_market: AsyncMarket, stubbed_payer: Keypair):
|
||||
await bootstrapped_market.match_orders(stubbed_payer, 2, TxOpts(skip_confirmation=False))
|
||||
await bootstrapped_market.match_orders(
|
||||
stubbed_payer, 2, TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
request_queue = await bootstrapped_market.load_request_queue()
|
||||
# 0 request after matching.
|
||||
|
@ -97,7 +107,9 @@ async def test_settle_fund(
|
|||
stubbed_quote_wallet: Keypair,
|
||||
stubbed_base_wallet: Keypair,
|
||||
):
|
||||
open_order_accounts = await bootstrapped_market.find_open_orders_accounts_for_owner(stubbed_payer.public_key)
|
||||
open_order_accounts = await bootstrapped_market.find_open_orders_accounts_for_owner(
|
||||
stubbed_payer.pubkey()
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
# Should not allow base_wallet to be base_vault
|
||||
|
@ -105,7 +117,7 @@ async def test_settle_fund(
|
|||
stubbed_payer,
|
||||
open_order_accounts[0],
|
||||
bootstrapped_market.state.base_vault(),
|
||||
stubbed_quote_wallet.public_key,
|
||||
stubbed_quote_wallet.pubkey(),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
|
@ -113,7 +125,7 @@ async def test_settle_fund(
|
|||
await bootstrapped_market.settle_funds(
|
||||
stubbed_payer,
|
||||
open_order_accounts[0],
|
||||
stubbed_base_wallet.public_key,
|
||||
stubbed_base_wallet.pubkey(),
|
||||
bootstrapped_market.state.quote_vault(),
|
||||
)
|
||||
|
||||
|
@ -121,8 +133,8 @@ async def test_settle_fund(
|
|||
assert "error" not in await bootstrapped_market.settle_funds(
|
||||
stubbed_payer,
|
||||
open_order_account,
|
||||
stubbed_base_wallet.public_key,
|
||||
stubbed_quote_wallet.public_key,
|
||||
stubbed_base_wallet.pubkey(),
|
||||
stubbed_quote_wallet.pubkey(),
|
||||
opts=TxOpts(skip_confirmation=False),
|
||||
)
|
||||
|
||||
|
@ -139,7 +151,7 @@ async def test_order_placement_cancellation_cycle(
|
|||
):
|
||||
initial_request_len = len(await bootstrapped_market.load_request_queue())
|
||||
await bootstrapped_market.place_order(
|
||||
payer=stubbed_quote_wallet.public_key,
|
||||
payer=stubbed_quote_wallet.pubkey(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.BUY,
|
||||
order_type=OrderType.LIMIT,
|
||||
|
@ -161,7 +173,7 @@ async def test_order_placement_cancellation_cycle(
|
|||
assert sum(1 for _ in asks) == 0
|
||||
|
||||
await bootstrapped_market.place_order(
|
||||
payer=stubbed_base_wallet.public_key,
|
||||
payer=stubbed_base_wallet.pubkey(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.SELL,
|
||||
order_type=OrderType.LIMIT,
|
||||
|
@ -186,18 +198,26 @@ async def test_order_placement_cancellation_cycle(
|
|||
assert sum(1 for _ in asks) == 1
|
||||
|
||||
for bid in bids:
|
||||
await bootstrapped_market.cancel_order(stubbed_payer, bid, opts=TxOpts(skip_confirmation=False))
|
||||
await bootstrapped_market.cancel_order(
|
||||
stubbed_payer, bid, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
await bootstrapped_market.match_orders(stubbed_payer, 1, opts=TxOpts(skip_confirmation=False))
|
||||
await bootstrapped_market.match_orders(
|
||||
stubbed_payer, 1, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
# All bid order should have been cancelled.
|
||||
bids = await bootstrapped_market.load_bids()
|
||||
assert sum(1 for _ in bids) == 0
|
||||
|
||||
for ask in asks:
|
||||
await bootstrapped_market.cancel_order(stubbed_payer, ask, opts=TxOpts(skip_confirmation=False))
|
||||
await bootstrapped_market.cancel_order(
|
||||
stubbed_payer, ask, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
await bootstrapped_market.match_orders(stubbed_payer, 1, opts=TxOpts(skip_confirmation=False))
|
||||
await bootstrapped_market.match_orders(
|
||||
stubbed_payer, 1, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
# All ask order should have been cancelled.
|
||||
asks = await bootstrapped_market.load_asks()
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import pytest
|
||||
|
||||
# from solana.account import Keypair
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -22,7 +21,7 @@ def test_base_wallet(stubbed_base_wallet):
|
|||
|
||||
@pytest.mark.integration
|
||||
def test_base_vault_pk(stubbed_base_vault_pk):
|
||||
assert isinstance(stubbed_base_vault_pk, PublicKey)
|
||||
assert isinstance(stubbed_base_vault_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -37,44 +36,44 @@ def test_quote_wallet(stubbed_quote_wallet):
|
|||
|
||||
@pytest.mark.integration
|
||||
def test_quote_vault_pk(stubbed_quote_vault_pk):
|
||||
assert isinstance(stubbed_quote_vault_pk, PublicKey)
|
||||
assert isinstance(stubbed_quote_vault_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_market_pk(stubbed_market_pk):
|
||||
assert isinstance(stubbed_market_pk, PublicKey)
|
||||
assert isinstance(stubbed_market_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_event_q_pk(stubbed_event_q_pk):
|
||||
assert isinstance(stubbed_event_q_pk, PublicKey)
|
||||
assert isinstance(stubbed_event_q_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_req_q_pk(stubbed_req_q_pk):
|
||||
assert isinstance(stubbed_req_q_pk, PublicKey)
|
||||
assert isinstance(stubbed_req_q_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_bids_pk(stubbed_bids_pk):
|
||||
assert isinstance(stubbed_bids_pk, PublicKey)
|
||||
assert isinstance(stubbed_bids_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_asks_pk(stubbed_asks_pk):
|
||||
assert isinstance(stubbed_asks_pk, PublicKey)
|
||||
assert isinstance(stubbed_asks_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_bid_account_pk(stubbed_bid_account_pk):
|
||||
assert isinstance(stubbed_bid_account_pk, PublicKey)
|
||||
assert isinstance(stubbed_bid_account_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_ask_account_pk(stubbed_ask_account_pk):
|
||||
assert isinstance(stubbed_ask_account_pk, PublicKey)
|
||||
assert isinstance(stubbed_ask_account_pk, Pubkey)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_dex_program_pk(stubbed_dex_program_pk):
|
||||
assert isinstance(stubbed_dex_program_pk, PublicKey)
|
||||
assert isinstance(stubbed_dex_program_pk, Pubkey)
|
||||
|
|
|
@ -7,7 +7,9 @@ from pyserum.market.types import MarketInfo, TokenInfo
|
|||
@pytest.mark.integration
|
||||
def test_get_live_markets():
|
||||
"""Test get_live_markets."""
|
||||
assert all(isinstance(market_info, MarketInfo) for market_info in get_live_markets())
|
||||
assert all(
|
||||
isinstance(market_info, MarketInfo) for market_info in get_live_markets()
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# pylint: disable=redefined-outer-name
|
||||
|
||||
import pytest
|
||||
from solana.keypair import Keypair
|
||||
from solana.publickey import PublicKey
|
||||
from solders.keypair import Keypair
|
||||
from solders.pubkey import Pubkey
|
||||
from solana.rpc.api import Client
|
||||
from solana.rpc.types import TxOpts
|
||||
|
||||
|
@ -12,23 +12,30 @@ from pyserum.market import Market
|
|||
|
||||
@pytest.mark.integration
|
||||
@pytest.fixture(scope="module")
|
||||
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, force_use_request_queue=True)
|
||||
def bootstrapped_market(
|
||||
http_client: Client, stubbed_market_pk: Pubkey, stubbed_dex_program_pk: Pubkey
|
||||
) -> Market:
|
||||
return Market.load(
|
||||
http_client,
|
||||
stubbed_market_pk,
|
||||
stubbed_dex_program_pk,
|
||||
force_use_request_queue=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_bootstrapped_market(
|
||||
bootstrapped_market: Market,
|
||||
stubbed_market_pk: PublicKey,
|
||||
stubbed_dex_program_pk: PublicKey,
|
||||
stubbed_market_pk: Pubkey,
|
||||
stubbed_dex_program_pk: Pubkey,
|
||||
stubbed_base_mint: Keypair,
|
||||
stubbed_quote_mint: Keypair,
|
||||
):
|
||||
assert isinstance(bootstrapped_market, Market)
|
||||
assert bootstrapped_market.state.public_key() == stubbed_market_pk
|
||||
assert bootstrapped_market.state.program_id() == stubbed_dex_program_pk
|
||||
assert bootstrapped_market.state.base_mint() == stubbed_base_mint.public_key
|
||||
assert bootstrapped_market.state.quote_mint() == stubbed_quote_mint.public_key
|
||||
assert bootstrapped_market.state.base_mint() == stubbed_base_mint.pubkey()
|
||||
assert bootstrapped_market.state.quote_mint() == stubbed_quote_mint.pubkey()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
@ -86,7 +93,9 @@ def test_settle_fund(
|
|||
stubbed_quote_wallet: Keypair,
|
||||
stubbed_base_wallet: Keypair,
|
||||
):
|
||||
open_order_accounts = bootstrapped_market.find_open_orders_accounts_for_owner(stubbed_payer.public_key)
|
||||
open_order_accounts = bootstrapped_market.find_open_orders_accounts_for_owner(
|
||||
stubbed_payer.pubkey()
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
# Should not allow base_wallet to be base_vault
|
||||
|
@ -94,7 +103,7 @@ def test_settle_fund(
|
|||
stubbed_payer,
|
||||
open_order_accounts[0],
|
||||
bootstrapped_market.state.base_vault(),
|
||||
stubbed_quote_wallet.public_key,
|
||||
stubbed_quote_wallet.pubkey(),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
|
@ -102,7 +111,7 @@ def test_settle_fund(
|
|||
bootstrapped_market.settle_funds(
|
||||
stubbed_payer,
|
||||
open_order_accounts[0],
|
||||
stubbed_base_wallet.public_key,
|
||||
stubbed_base_wallet.pubkey(),
|
||||
bootstrapped_market.state.quote_vault(),
|
||||
)
|
||||
|
||||
|
@ -110,8 +119,8 @@ def test_settle_fund(
|
|||
assert "error" not in bootstrapped_market.settle_funds(
|
||||
stubbed_payer,
|
||||
open_order_account,
|
||||
stubbed_base_wallet.public_key,
|
||||
stubbed_quote_wallet.public_key,
|
||||
stubbed_base_wallet.pubkey(),
|
||||
stubbed_quote_wallet.pubkey(),
|
||||
opts=TxOpts(skip_confirmation=False),
|
||||
)
|
||||
|
||||
|
@ -127,7 +136,7 @@ def test_order_placement_cancellation_cycle(
|
|||
):
|
||||
initial_request_len = len(bootstrapped_market.load_request_queue())
|
||||
bootstrapped_market.place_order(
|
||||
payer=stubbed_quote_wallet.public_key,
|
||||
payer=stubbed_quote_wallet.pubkey(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.BUY,
|
||||
order_type=OrderType.LIMIT,
|
||||
|
@ -149,7 +158,7 @@ def test_order_placement_cancellation_cycle(
|
|||
assert sum(1 for _ in asks) == 0
|
||||
|
||||
bootstrapped_market.place_order(
|
||||
payer=stubbed_base_wallet.public_key,
|
||||
payer=stubbed_base_wallet.pubkey(),
|
||||
owner=stubbed_payer,
|
||||
side=Side.SELL,
|
||||
order_type=OrderType.LIMIT,
|
||||
|
@ -174,18 +183,26 @@ def test_order_placement_cancellation_cycle(
|
|||
assert sum(1 for _ in asks) == 1
|
||||
|
||||
for bid in bids:
|
||||
bootstrapped_market.cancel_order(stubbed_payer, bid, opts=TxOpts(skip_confirmation=False))
|
||||
bootstrapped_market.cancel_order(
|
||||
stubbed_payer, bid, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
bootstrapped_market.match_orders(stubbed_payer, 1, opts=TxOpts(skip_confirmation=False))
|
||||
bootstrapped_market.match_orders(
|
||||
stubbed_payer, 1, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
# All bid order should have been cancelled.
|
||||
bids = bootstrapped_market.load_bids()
|
||||
assert sum(1 for _ in bids) == 0
|
||||
|
||||
for ask in asks:
|
||||
bootstrapped_market.cancel_order(stubbed_payer, ask, opts=TxOpts(skip_confirmation=False))
|
||||
bootstrapped_market.cancel_order(
|
||||
stubbed_payer, ask, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
bootstrapped_market.match_orders(stubbed_payer, 1, opts=TxOpts(skip_confirmation=False))
|
||||
bootstrapped_market.match_orders(
|
||||
stubbed_payer, 1, opts=TxOpts(skip_confirmation=False)
|
||||
)
|
||||
|
||||
# All ask order should have been cancelled.
|
||||
asks = bootstrapped_market.load_asks()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""Tests for instruction layouts."""
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
from pyserum._layouts.instructions import _VERSION, INSTRUCTIONS_LAYOUT, InstructionType
|
||||
from pyserum.enums import OrderType, Side
|
||||
|
@ -27,7 +27,12 @@ def test_parse_initialize_market():
|
|||
expected = bytes.fromhex(
|
||||
"000000000001000000000000000200000000000000030004000000000000000500000000000000"
|
||||
) # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.INITIALIZE_MARKET, args=args)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.INITIALIZE_MARKET, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.INITIALIZE_MARKET, args, expected)
|
||||
|
||||
|
||||
|
@ -43,7 +48,12 @@ def test_parse_new_order():
|
|||
expected = bytes.fromhex(
|
||||
"00010000000100000001000000000000000200000000000000020000000300000000000000"
|
||||
) # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.NEW_ORDER, args=args)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.NEW_ORDER, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.NEW_ORDER, args, expected)
|
||||
|
||||
|
||||
|
@ -51,7 +61,12 @@ def test_parse_match_orders():
|
|||
"""Test parsing raw match orders data."""
|
||||
args = {"limit": 1}
|
||||
expected = bytes.fromhex("00020000000100") # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.MATCH_ORDER, args=args)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.MATCH_ORDER, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.MATCH_ORDER, args, expected)
|
||||
|
||||
|
||||
|
@ -59,7 +74,12 @@ def test_parse_consume_events():
|
|||
"""Test parsing raw consume events data."""
|
||||
args = {"limit": 1}
|
||||
expected = bytes.fromhex("00030000000100") # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.CONSUME_EVENTS, args=args)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.CONSUME_EVENTS, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.CONSUME_EVENTS, args, expected)
|
||||
|
||||
|
||||
|
@ -69,20 +89,30 @@ def test_parse_cancel_order():
|
|||
"side": Side.BUY,
|
||||
"order_id": (1234567890).to_bytes(16, "little"),
|
||||
"open_orders_slot": 123,
|
||||
"open_orders": bytes(PublicKey(123)),
|
||||
"open_orders": bytes(Pubkey.from_string("111111111111111111111111111111138")),
|
||||
}
|
||||
expected = bytes.fromhex(
|
||||
"000400000000000000d202964900000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000007b7b"
|
||||
) # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.CANCEL_ORDER, args=args)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.CANCEL_ORDER, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.CANCEL_ORDER, args, expected)
|
||||
|
||||
|
||||
def test_parse_settle_funds():
|
||||
"""Test parsing raw settle funds data."""
|
||||
expected = bytes.fromhex("0005000000") # Raw hex from serum.js
|
||||
assert INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.SETTLE_FUNDS, args=None)) == expected
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.SETTLE_FUNDS, args=None)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.SETTLE_FUNDS, None, expected)
|
||||
|
||||
|
||||
|
@ -91,7 +121,9 @@ def test_parse_cancel_order_by_client_id():
|
|||
args = {"client_id": 123}
|
||||
expected = bytes.fromhex("00060000007b00000000000000") # Raw hex from serum.js
|
||||
assert (
|
||||
INSTRUCTIONS_LAYOUT.build(dict(instruction_type=InstructionType.CANCEL_ORDER_BY_CLIENT_ID, args=args))
|
||||
INSTRUCTIONS_LAYOUT.build(
|
||||
dict(instruction_type=InstructionType.CANCEL_ORDER_BY_CLIENT_ID, args=args)
|
||||
)
|
||||
== expected
|
||||
)
|
||||
assert_parsed_layout(InstructionType.CANCEL_ORDER_BY_CLIENT_ID, args, expected)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Test instructions."""
|
||||
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
import pyserum.instructions as inlib
|
||||
from pyserum.enums import OrderType, Side
|
||||
|
@ -9,15 +9,15 @@ from pyserum.enums import OrderType, Side
|
|||
def test_initialize_market():
|
||||
"""Test initialize market."""
|
||||
params = inlib.InitializeMarketParams(
|
||||
market=PublicKey(0),
|
||||
request_queue=PublicKey(1),
|
||||
event_queue=PublicKey(2),
|
||||
bids=PublicKey(3),
|
||||
asks=PublicKey(4),
|
||||
base_vault=PublicKey(5),
|
||||
quote_vault=PublicKey(6),
|
||||
base_mint=PublicKey(7),
|
||||
quote_mint=PublicKey(8),
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
request_queue=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
event_queue=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
bids=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
asks=Pubkey.from_string("11111111111111111111111111111116"),
|
||||
base_vault=Pubkey.from_string("11111111111111111111111111111117"),
|
||||
quote_vault=Pubkey.from_string("11111111111111111111111111111118"),
|
||||
base_mint=Pubkey.from_string("11111111111111111111111111111119"),
|
||||
quote_mint=Pubkey.from_string("1111111111111111111111111111111A"),
|
||||
base_lot_size=1,
|
||||
quote_lot_size=2,
|
||||
fee_rate_bps=3,
|
||||
|
@ -31,13 +31,13 @@ def test_initialize_market():
|
|||
def test_new_orders():
|
||||
"""Test match orders."""
|
||||
params = inlib.NewOrderParams(
|
||||
market=PublicKey(0),
|
||||
open_orders=PublicKey(1),
|
||||
payer=PublicKey(2),
|
||||
owner=PublicKey(3),
|
||||
request_queue=PublicKey(4),
|
||||
base_vault=PublicKey(5),
|
||||
quote_vault=PublicKey(6),
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
payer=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
request_queue=Pubkey.from_string("11111111111111111111111111111116"),
|
||||
base_vault=Pubkey.from_string("11111111111111111111111111111117"),
|
||||
quote_vault=Pubkey.from_string("11111111111111111111111111111118"),
|
||||
side=Side.BUY,
|
||||
limit_price=1,
|
||||
max_quantity=1,
|
||||
|
@ -51,13 +51,13 @@ def test_new_orders():
|
|||
def test_match_orders():
|
||||
"""Test match orders."""
|
||||
params = inlib.MatchOrdersParams(
|
||||
market=PublicKey(0),
|
||||
request_queue=PublicKey(1),
|
||||
event_queue=PublicKey(2),
|
||||
bids=PublicKey(3),
|
||||
asks=PublicKey(4),
|
||||
base_vault=PublicKey(5),
|
||||
quote_vault=PublicKey(6),
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
request_queue=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
event_queue=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
bids=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
asks=Pubkey.from_string("11111111111111111111111111111116"),
|
||||
base_vault=Pubkey.from_string("11111111111111111111111111111117"),
|
||||
quote_vault=Pubkey.from_string("11111111111111111111111111111118"),
|
||||
limit=1,
|
||||
)
|
||||
instruction = inlib.match_orders(params)
|
||||
|
@ -66,9 +66,12 @@ def test_match_orders():
|
|||
|
||||
def test_consume_events():
|
||||
params = inlib.ConsumeEventsParams(
|
||||
market=PublicKey(0),
|
||||
event_queue=PublicKey(1),
|
||||
open_orders_accounts=[PublicKey(i + 2) for i in range(8)],
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
event_queue=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
open_orders_accounts=[
|
||||
Pubkey.from_string("1111111111111111111111111111111{:X}".format(i + 2))
|
||||
for i in range(8)
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
instruction = inlib.consume_events(params)
|
||||
|
@ -78,10 +81,10 @@ def test_consume_events():
|
|||
def test_cancel_order():
|
||||
"""Test cancel order."""
|
||||
params = inlib.CancelOrderParams(
|
||||
market=PublicKey(0),
|
||||
request_queue=PublicKey(1),
|
||||
owner=PublicKey(2),
|
||||
open_orders=PublicKey(3),
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
request_queue=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
side=Side.BUY,
|
||||
order_id=1,
|
||||
open_orders_slot=1,
|
||||
|
@ -93,7 +96,11 @@ def test_cancel_order():
|
|||
def test_cancel_order_by_client_id():
|
||||
"""Test cancel order by client id."""
|
||||
params = inlib.CancelOrderByClientIDParams(
|
||||
market=PublicKey(0), request_queue=PublicKey(1), owner=PublicKey(2), open_orders=PublicKey(3), client_id=1
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
request_queue=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
client_id=1,
|
||||
)
|
||||
instruction = inlib.cancel_order_by_client_id(params)
|
||||
assert inlib.decode_cancel_order_by_client_id(instruction) == params
|
||||
|
@ -102,14 +109,14 @@ def test_cancel_order_by_client_id():
|
|||
def test_settle_funds():
|
||||
"""Test settle funds."""
|
||||
params = inlib.SettleFundsParams(
|
||||
market=PublicKey(0),
|
||||
owner=PublicKey(1),
|
||||
open_orders=PublicKey(2),
|
||||
base_vault=PublicKey(3),
|
||||
quote_vault=PublicKey(4),
|
||||
base_wallet=PublicKey(5),
|
||||
quote_wallet=PublicKey(6),
|
||||
vault_signer=PublicKey(7),
|
||||
market=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
base_vault=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
quote_vault=Pubkey.from_string("11111111111111111111111111111116"),
|
||||
base_wallet=Pubkey.from_string("11111111111111111111111111111117"),
|
||||
quote_wallet=Pubkey.from_string("11111111111111111111111111111118"),
|
||||
vault_signer=Pubkey.from_string("11111111111111111111111111111119"),
|
||||
)
|
||||
instruction = inlib.settle_funds(params)
|
||||
assert inlib.decode_settle_funds(instruction) == params
|
||||
|
@ -118,10 +125,10 @@ def test_settle_funds():
|
|||
def test_close_open_orders():
|
||||
"""Test settle funds."""
|
||||
params = inlib.CloseOpenOrdersParams(
|
||||
open_orders=PublicKey(0),
|
||||
owner=PublicKey(1),
|
||||
sol_wallet=PublicKey(2),
|
||||
market=PublicKey(3),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
sol_wallet=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
market=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
)
|
||||
instruction = inlib.close_open_orders(params)
|
||||
assert inlib.decode_close_open_orders(instruction) == params
|
||||
|
@ -130,7 +137,10 @@ def test_close_open_orders():
|
|||
def test_init_open_orders():
|
||||
"""Test settle funds."""
|
||||
params = inlib.InitOpenOrdersParams(
|
||||
open_orders=PublicKey(0), owner=PublicKey(1), market=PublicKey(2), market_authority=None
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
market=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
market_authority=None,
|
||||
)
|
||||
instruction = inlib.init_open_orders(params)
|
||||
assert inlib.decode_init_open_orders(instruction) == params
|
||||
|
@ -139,10 +149,10 @@ def test_init_open_orders():
|
|||
def test_init_open_orders_with_authority():
|
||||
"""Test settle funds."""
|
||||
params = inlib.InitOpenOrdersParams(
|
||||
open_orders=PublicKey(0),
|
||||
owner=PublicKey(1),
|
||||
market=PublicKey(2),
|
||||
market_authority=PublicKey(3),
|
||||
open_orders=Pubkey.from_string("11111111111111111111111111111112"),
|
||||
owner=Pubkey.from_string("11111111111111111111111111111113"),
|
||||
market=Pubkey.from_string("11111111111111111111111111111114"),
|
||||
market_authority=Pubkey.from_string("11111111111111111111111111111115"),
|
||||
)
|
||||
instruction = inlib.init_open_orders(params)
|
||||
assert inlib.decode_init_open_orders(instruction) == params
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import base64
|
||||
|
||||
import pytest
|
||||
from solana.publickey import PublicKey
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
from pyserum.open_orders_account import OPEN_ORDERS_LAYOUT, OpenOrdersAccount
|
||||
|
||||
|
@ -19,10 +19,23 @@ def test_decode_open_order_account_layout():
|
|||
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")
|
||||
assert Pubkey.from_bytes(open_order_account.market) == Pubkey.from_string(
|
||||
"4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2"
|
||||
)
|
||||
assert Pubkey.from_bytes(open_order_account.owner) == Pubkey.from_string(
|
||||
"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
|
||||
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
|
||||
|
||||
|
@ -35,9 +48,15 @@ def test_decode_open_order_account():
|
|||
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 = OpenOrdersAccount.from_bytes(PublicKey(1), data)
|
||||
assert open_order_account.market == PublicKey("4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2")
|
||||
assert open_order_account.owner == PublicKey("7hJx7QMiVfjZSSADQ18oNKzqifJPMu18djYLkh4aYh5Q")
|
||||
open_order_account = OpenOrdersAccount.from_bytes(
|
||||
Pubkey.from_string("11111111111111111111111111111112"), data
|
||||
)
|
||||
assert open_order_account.market == Pubkey.from_string(
|
||||
"4r5Bw3HxmxAzPQ2ATUvgF2nFe3B6G1Z2Nq2Nwu77wWc2"
|
||||
)
|
||||
assert open_order_account.owner == Pubkey.from_string(
|
||||
"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
|
||||
|
|
|
@ -2,7 +2,12 @@
|
|||
|
||||
import base64
|
||||
|
||||
from pyserum._layouts.slab import ORDER_BOOK_LAYOUT, SLAB_HEADER_LAYOUT, SLAB_LAYOUT, SLAB_NODE_LAYOUT
|
||||
from pyserum._layouts.slab import (
|
||||
ORDER_BOOK_LAYOUT,
|
||||
SLAB_HEADER_LAYOUT,
|
||||
SLAB_LAYOUT,
|
||||
SLAB_NODE_LAYOUT,
|
||||
)
|
||||
from pyserum.market._internal.slab import Slab
|
||||
|
||||
from .binary_file_path import ASK_ORDER_BIN_PATH
|
||||
|
|
Loading…
Reference in New Issue