Merge pull request #8 from switchboard-xyz/6-python-wrapper-fix

6 python wrapper fix
This commit is contained in:
gallynaut 2022-06-10 10:01:09 -06:00 committed by GitHub
commit b580a2beb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 979 additions and 609 deletions

474
libraries/py/poetry.lock generated
View File

@ -1,6 +1,6 @@
[[package]] [[package]]
name = "anchorpy" name = "anchorpy"
version = "0.9.1" version = "0.9.2"
description = "The Python Anchor client." description = "The Python Anchor client."
category = "main" category = "main"
optional = false optional = false
@ -8,12 +8,8 @@ python-versions = ">=3.9,<4.0"
[package.dependencies] [package.dependencies]
apischema = ">=0.16.6,<0.17.0" apischema = ">=0.16.6,<0.17.0"
autoflake = {version = ">=1.4,<2.0", optional = true, markers = "extra == \"cli\""}
black = {version = ">=22.3.0,<23.0.0", optional = true, markers = "extra == \"cli\""}
borsh-construct = ">=0.1.0,<0.2.0" borsh-construct = ">=0.1.0,<0.2.0"
construct-typing = ">=0.5.1,<0.6.0" construct-typing = ">=0.5.1,<0.6.0"
genpy = {version = ">=2021.1,<2022.0", optional = true, markers = "extra == \"cli\""}
ipython = {version = ">=8.0.1,<9.0.0", optional = true, markers = "extra == \"cli\""}
jsonrpcclient = ">=4.0.1,<5.0.0" jsonrpcclient = ">=4.0.1,<5.0.0"
more-itertools = ">=8.11.0,<9.0.0" more-itertools = ">=8.11.0,<9.0.0"
pyheck = ">=0.1.4,<0.2.0" pyheck = ">=0.1.4,<0.2.0"
@ -23,7 +19,6 @@ pytest-xprocess = ">=0.18.1,<0.19.0"
solana = ">=0.23.1,<0.23.3" solana = ">=0.23.1,<0.23.3"
sumtypes = ">=0.1a6,<0.2" sumtypes = ">=0.1a6,<0.2"
toolz = ">=0.11.2,<0.12.0" toolz = ">=0.11.2,<0.12.0"
typer = {version = "0.4.1", optional = true, markers = "extra == \"cli\""}
websockets = ">=10.0,<11.0" websockets = ">=10.0,<11.0"
zstandard = ">=0.17.0,<0.18.0" zstandard = ">=0.17.0,<0.18.0"
@ -59,28 +54,6 @@ python-versions = ">=3.6"
examples = ["graphql-core (>=3.0.0)", "attrs", "docstring-parser", "bson", "orjson", "pydantic", "pytest", "sqlalchemy"] examples = ["graphql-core (>=3.0.0)", "attrs", "docstring-parser", "bson", "orjson", "pydantic", "pytest", "sqlalchemy"]
graphql = ["graphql-core (>=3.0.0)"] graphql = ["graphql-core (>=3.0.0)"]
[[package]]
name = "appnope"
version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "asttokens"
version = "2.0.5"
description = "Annotate AST trees with source code positions"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
six = "*"
[package.extras]
test = ["astroid", "pytest"]
[[package]] [[package]]
name = "atomicwrites" name = "atomicwrites"
version = "1.4.0" version = "1.4.0"
@ -103,25 +76,6 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
[[package]]
name = "autoflake"
version = "1.4"
description = "Removes unused imports and unused variables"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
pyflakes = ">=1.1.0"
[[package]]
name = "backcall"
version = "0.2.0"
description = "Specifications for callback functions passed in to an API"
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "base58" name = "base58"
version = "2.1.1" version = "2.1.1"
@ -141,28 +95,6 @@ category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[[package]]
name = "black"
version = "22.3.0"
description = "The uncompromising code formatter."
category = "main"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]] [[package]]
name = "borsh-construct" name = "borsh-construct"
version = "0.1.0" version = "0.1.0"
@ -254,34 +186,6 @@ python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
construct = "2.10.67" construct = "2.10.67"
[[package]]
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "executing"
version = "0.8.3"
description = "Get the currently executing AST node of a frame, and other information"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "genpy"
version = "2021.1"
description = "AST-based generation of Python source code"
category = "main"
optional = false
python-versions = "~=3.6"
[package.dependencies]
numpy = ">=1.6"
pytools = ">=2015.1.2"
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.12.0" version = "0.12.0"
@ -348,56 +252,6 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "ipython"
version = "8.4.0"
description = "IPython: Productive Interactive Computing"
category = "main"
optional = false
python-versions = ">=3.8"
[package.dependencies]
appnope = {version = "*", markers = "sys_platform == \"darwin\""}
backcall = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
jedi = ">=0.16"
matplotlib-inline = "*"
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
pickleshare = "*"
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
pygments = ">=2.4.0"
stack-data = "*"
traitlets = ">=5"
[package.extras]
all = ["black", "Sphinx (>=1.3)", "ipykernel", "nbconvert", "nbformat", "ipywidgets", "notebook", "ipyparallel", "qtconsole", "pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "numpy (>=1.19)", "pandas", "trio"]
black = ["black"]
doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
test_extra = ["pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "trio"]
[[package]]
name = "jedi"
version = "0.18.1"
description = "An autocompletion tool for Python that can be used for text editors."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
parso = ">=0.8.0,<0.9.0"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]] [[package]]
name = "jinja2" name = "jinja2"
version = "3.1.1" version = "3.1.1"
@ -460,17 +314,6 @@ category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[[package]]
name = "matplotlib-inline"
version = "0.1.3"
description = "Inline Matplotlib backend for Jupyter"
category = "main"
optional = false
python-versions = ">=3.5"
[package.dependencies]
traitlets = "*"
[[package]] [[package]]
name = "more-itertools" name = "more-itertools"
version = "8.12.0" version = "8.12.0"
@ -479,22 +322,6 @@ category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "numpy"
version = "1.22.4"
description = "NumPy is the fundamental package for array computing with Python."
category = "main"
optional = false
python-versions = ">=3.8"
[[package]] [[package]]
name = "oslash" name = "oslash"
version = "0.6.3" version = "0.6.3"
@ -517,26 +344,6 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "pathspec"
version = "0.9.0"
description = "Utility library for gitignore style pattern matching of file paths."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[[package]] [[package]]
name = "pdoc" name = "pdoc"
version = "11.0.0" version = "11.0.0"
@ -553,37 +360,6 @@ pygments = "*"
[package.extras] [package.extras]
dev = ["flake8", "hypothesis", "mypy", "pytest", "pytest-cov", "pytest-timeout", "tox"] dev = ["flake8", "hypothesis", "mypy", "pytest", "pytest-cov", "pytest-timeout", "tox"]
[[package]]
name = "pexpect"
version = "4.8.0"
description = "Pexpect allows easy control of interactive console applications."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
ptyprocess = ">=0.5"
[[package]]
name = "pickleshare"
version = "0.7.5"
description = "Tiny 'shelve'-like database with concurrency support"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "platformdirs"
version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.0.0" version = "1.0.0"
@ -596,17 +372,6 @@ python-versions = ">=3.6"
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"] testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "prompt-toolkit"
version = "3.0.29"
description = "Library for building powerful interactive command lines in Python"
category = "main"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
wcwidth = "*"
[[package]] [[package]]
name = "protobuf" name = "protobuf"
version = "3.20.0" version = "3.20.0"
@ -626,25 +391,6 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras] [package.extras]
test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
[[package]]
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "pure-eval"
version = "0.2.2"
description = "Safely evaluate AST nodes without side effects"
category = "main"
optional = false
python-versions = "*"
[package.extras]
tests = ["pytest"]
[[package]] [[package]]
name = "py" name = "py"
version = "1.11.0" version = "1.11.0"
@ -661,19 +407,11 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.11.2" version = "2.11.2"
description = "Pygments is a syntax highlighting package written in Python." description = "Pygments is a syntax highlighting package written in Python."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
@ -767,19 +505,6 @@ python-versions = ">=3.5"
psutil = "*" psutil = "*"
pytest = ">=2.8" pytest = ">=2.8"
[[package]]
name = "pytools"
version = "2022.1.4"
description = "A collection of tools for Python"
category = "main"
optional = false
python-versions = "~=3.6"
[package.dependencies]
numpy = ">=1.6.0"
platformdirs = ">=2.2.0"
typing_extensions = {version = "*", markers = "python_version < \"3.11\""}
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.26.0" version = "2.26.0"
@ -850,22 +575,6 @@ types-cachetools = ">=4.2.4,<5.0.0"
typing-extensions = ">=3.10.0" typing-extensions = ">=3.10.0"
websockets = ">=10.1,<11.0" websockets = ">=10.1,<11.0"
[[package]]
name = "stack-data"
version = "0.2.0"
description = "Extract data from python stack frames and tracebacks for informative displays"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
asttokens = "*"
executing = "*"
pure-eval = "*"
[package.extras]
tests = ["pytest", "typeguard", "pygments", "littleutils", "cython"]
[[package]] [[package]]
name = "sumtypes" name = "sumtypes"
version = "0.1a6" version = "0.1a6"
@ -885,14 +594,6 @@ category = "main"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "main"
optional = false
python-versions = ">=3.7"
[[package]] [[package]]
name = "toolz" name = "toolz"
version = "0.11.2" version = "0.11.2"
@ -901,17 +602,6 @@ category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
[[package]]
name = "traitlets"
version = "5.2.2.post1"
description = ""
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
test = ["pre-commit", "pytest"]
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.4.1" version = "0.4.1"
@ -958,14 +648,6 @@ brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "wcwidth"
version = "0.2.5"
description = "Measures the displayed width of unicode strings in a terminal"
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "websockets" name = "websockets"
version = "10.1" version = "10.1"
@ -991,12 +673,12 @@ cffi = ["cffi (>=1.11)"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "49f6a90d0ae1e8cb1070d767f1c8e63bda4da0bcbe1780a8f8f6af5e555957bd" content-hash = "45003fc29eae952b587685f3106b4f7e95f1169cef8ccf5585b69af5cf3f0751"
[metadata.files] [metadata.files]
anchorpy = [ anchorpy = [
{file = "anchorpy-0.9.1-py3-none-any.whl", hash = "sha256:889af502605a20c8c4cc21e71396163ad42c4f2da6b0a6599f166aca23e70676"}, {file = "anchorpy-0.9.2-py3-none-any.whl", hash = "sha256:d977ada7b800507b05ebcec43f4dbcbbb1217e47cf3eda5cb30b32e47e997bc2"},
{file = "anchorpy-0.9.1.tar.gz", hash = "sha256:172e51b13b42a92b006eb5d91af5430ed95353b6acb07e2112603dbac545126e"}, {file = "anchorpy-0.9.2.tar.gz", hash = "sha256:dd1619e4322d9dbeeaafe648b5559cd6051b393cda62fe9694537ba6a529e563"},
] ]
anyio = [ anyio = [
{file = "anyio-3.4.0-py3-none-any.whl", hash = "sha256:2855a9423524abcdd652d942f8932fda1735210f77a6b392eafd9ff34d3fe020"}, {file = "anyio-3.4.0-py3-none-any.whl", hash = "sha256:2855a9423524abcdd652d942f8932fda1735210f77a6b392eafd9ff34d3fe020"},
@ -1006,14 +688,6 @@ apischema = [
{file = "apischema-0.16.6-py3-none-any.whl", hash = "sha256:b1ffcc19831fceb99c175ce53d81125bb46b8b22a019f4d8307f6d23f13e5647"}, {file = "apischema-0.16.6-py3-none-any.whl", hash = "sha256:b1ffcc19831fceb99c175ce53d81125bb46b8b22a019f4d8307f6d23f13e5647"},
{file = "apischema-0.16.6.tar.gz", hash = "sha256:5e53830269d17a3586103c71d7961f23df5fe8844f93afbcc3e7a1a7cbac0c75"}, {file = "apischema-0.16.6.tar.gz", hash = "sha256:5e53830269d17a3586103c71d7961f23df5fe8844f93afbcc3e7a1a7cbac0c75"},
] ]
appnope = [
{file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
{file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
]
asttokens = [
{file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"},
{file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"},
]
atomicwrites = [ atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
@ -1022,13 +696,6 @@ attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
] ]
autoflake = [
{file = "autoflake-1.4.tar.gz", hash = "sha256:61a353012cff6ab94ca062823d1fb2f692c4acda51c76ff83a8d77915fba51ea"},
]
backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
base58 = [ base58 = [
{file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"},
{file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"},
@ -1049,31 +716,6 @@ based58 = [
{file = "based58-0.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:ab85804a401a7b5a7141fbb14ef5b5f7d85288357d1d3f0085d47e616cef8f5a"}, {file = "based58-0.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:ab85804a401a7b5a7141fbb14ef5b5f7d85288357d1d3f0085d47e616cef8f5a"},
{file = "based58-0.1.1.tar.gz", hash = "sha256:80804b346b34196c89dc7a3dc89b6021f910f4cd75aac41d433ca1880b1672dc"}, {file = "based58-0.1.1.tar.gz", hash = "sha256:80804b346b34196c89dc7a3dc89b6021f910f4cd75aac41d433ca1880b1672dc"},
] ]
black = [
{file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
{file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
{file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
{file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
{file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
{file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
{file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
{file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
{file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
{file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
{file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
{file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
{file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
{file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
{file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
{file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
{file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
{file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
{file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
{file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
{file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
{file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
{file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
]
borsh-construct = [ borsh-construct = [
{file = "borsh-construct-0.1.0.tar.gz", hash = "sha256:c916758ceba70085d8f456a1cc26991b88cb64233d347767766473b651b37263"}, {file = "borsh-construct-0.1.0.tar.gz", hash = "sha256:c916758ceba70085d8f456a1cc26991b88cb64233d347767766473b651b37263"},
{file = "borsh_construct-0.1.0-py3-none-any.whl", hash = "sha256:f584c791e2a03f8fc36e6c13011a27bcaf028c9c54ba89cd70f485a7d1c687ed"}, {file = "borsh_construct-0.1.0-py3-none-any.whl", hash = "sha256:f584c791e2a03f8fc36e6c13011a27bcaf028c9c54ba89cd70f485a7d1c687ed"},
@ -1157,17 +799,6 @@ construct-typing = [
{file = "construct-typing-0.5.2.tar.gz", hash = "sha256:26da1ed7383c5dfe7f38a43c6281ad06bce2059bf02d5a0d2cc3c0e52632c0bd"}, {file = "construct-typing-0.5.2.tar.gz", hash = "sha256:26da1ed7383c5dfe7f38a43c6281ad06bce2059bf02d5a0d2cc3c0e52632c0bd"},
{file = "construct_typing-0.5.2-py3-none-any.whl", hash = "sha256:553ecf36b44ae8d87e2f9c28a6fc8ffd75d050bd0b66ac72e433046c45b52116"}, {file = "construct_typing-0.5.2-py3-none-any.whl", hash = "sha256:553ecf36b44ae8d87e2f9c28a6fc8ffd75d050bd0b66ac72e433046c45b52116"},
] ]
decorator = [
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
executing = [
{file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"},
{file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"},
]
genpy = [
{file = "genpy-2021.1.tar.gz", hash = "sha256:9bc062fa98c5c466ff464d8974be81a6bf67af9247b5e5176215ad1e81a6cdac"},
]
h11 = [ h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
@ -1192,14 +823,6 @@ iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
] ]
ipython = [
{file = "ipython-8.4.0-py3-none-any.whl", hash = "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1"},
{file = "ipython-8.4.0.tar.gz", hash = "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"},
]
jedi = [
{file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
{file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
]
jinja2 = [ jinja2 = [
{file = "Jinja2-3.1.1-py3-none-any.whl", hash = "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119"}, {file = "Jinja2-3.1.1-py3-none-any.whl", hash = "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119"},
{file = "Jinja2-3.1.1.tar.gz", hash = "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"}, {file = "Jinja2-3.1.1.tar.gz", hash = "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"},
@ -1256,42 +879,10 @@ markupsafe = [
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
] ]
matplotlib-inline = [
{file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
{file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
]
more-itertools = [ more-itertools = [
{file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"},
{file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"},
] ]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
numpy = [
{file = "numpy-1.22.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:ba9ead61dfb5d971d77b6c131a9dbee62294a932bf6a356e48c75ae684e635b3"},
{file = "numpy-1.22.4-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:1ce7ab2053e36c0a71e7a13a7475bd3b1f54750b4b433adc96313e127b870887"},
{file = "numpy-1.22.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7228ad13744f63575b3a972d7ee4fd61815b2879998e70930d4ccf9ec721dce0"},
{file = "numpy-1.22.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43a8ca7391b626b4c4fe20aefe79fec683279e31e7c79716863b4b25021e0e74"},
{file = "numpy-1.22.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a911e317e8c826ea632205e63ed8507e0dc877dcdc49744584dfc363df9ca08c"},
{file = "numpy-1.22.4-cp310-cp310-win32.whl", hash = "sha256:9ce7df0abeabe7fbd8ccbf343dc0db72f68549856b863ae3dd580255d009648e"},
{file = "numpy-1.22.4-cp310-cp310-win_amd64.whl", hash = "sha256:3e1ffa4748168e1cc8d3cde93f006fe92b5421396221a02f2274aab6ac83b077"},
{file = "numpy-1.22.4-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:59d55e634968b8f77d3fd674a3cf0b96e85147cd6556ec64ade018f27e9479e1"},
{file = "numpy-1.22.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c1d937820db6e43bec43e8d016b9b3165dcb42892ea9f106c70fb13d430ffe72"},
{file = "numpy-1.22.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4c5d5eb2ec8da0b4f50c9a843393971f31f1d60be87e0fb0917a49133d257d6"},
{file = "numpy-1.22.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64f56fc53a2d18b1924abd15745e30d82a5782b2cab3429aceecc6875bd5add0"},
{file = "numpy-1.22.4-cp38-cp38-win32.whl", hash = "sha256:fb7a980c81dd932381f8228a426df8aeb70d59bbcda2af075b627bbc50207cba"},
{file = "numpy-1.22.4-cp38-cp38-win_amd64.whl", hash = "sha256:e96d7f3096a36c8754207ab89d4b3282ba7b49ea140e4973591852c77d09eb76"},
{file = "numpy-1.22.4-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4c6036521f11a731ce0648f10c18ae66d7143865f19f7299943c985cdc95afb5"},
{file = "numpy-1.22.4-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b89bf9b94b3d624e7bb480344e91f68c1c6c75f026ed6755955117de00917a7c"},
{file = "numpy-1.22.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2d487e06ecbf1dc2f18e7efce82ded4f705f4bd0cd02677ffccfb39e5c284c7e"},
{file = "numpy-1.22.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eb268dbd5cfaffd9448113539e44e2dd1c5ca9ce25576f7c04a5453edc26fa"},
{file = "numpy-1.22.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37431a77ceb9307c28382c9773da9f306435135fae6b80b62a11c53cfedd8802"},
{file = "numpy-1.22.4-cp39-cp39-win32.whl", hash = "sha256:cc7f00008eb7d3f2489fca6f334ec19ca63e31371be28fd5dad955b16ec285bd"},
{file = "numpy-1.22.4-cp39-cp39-win_amd64.whl", hash = "sha256:f0725df166cf4785c0bc4cbfb320203182b1ecd30fee6e541c8752a92df6aa32"},
{file = "numpy-1.22.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0791fbd1e43bf74b3502133207e378901272f3c156c4df4954cad833b1380207"},
{file = "numpy-1.22.4.zip", hash = "sha256:425b390e4619f58d8526b3dcf656dde069133ae5c240229821f01b5f44ea07af"},
]
oslash = [ oslash = [
{file = "OSlash-0.6.3-py3-none-any.whl", hash = "sha256:89b978443b7db3ac2666106bdc3680add3c886a6d8fcdd02fd062af86d29494f"}, {file = "OSlash-0.6.3-py3-none-any.whl", hash = "sha256:89b978443b7db3ac2666106bdc3680add3c886a6d8fcdd02fd062af86d29494f"},
{file = "OSlash-0.6.3.tar.gz", hash = "sha256:868aeb58a656f2ed3b73d9dd6abe387b20b74fc9413d3e8653b615b15bf728f3"}, {file = "OSlash-0.6.3.tar.gz", hash = "sha256:868aeb58a656f2ed3b73d9dd6abe387b20b74fc9413d3e8653b615b15bf728f3"},
@ -1300,38 +891,14 @@ packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
] ]
parso = [
{file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
{file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]
pathspec = [
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
]
pdoc = [ pdoc = [
{file = "pdoc-11.0.0-py3-none-any.whl", hash = "sha256:36922f9eebf90a5e546ab4e7d92d4dbf9f2cdae7679fbc9b1a0906ef8ef03214"}, {file = "pdoc-11.0.0-py3-none-any.whl", hash = "sha256:36922f9eebf90a5e546ab4e7d92d4dbf9f2cdae7679fbc9b1a0906ef8ef03214"},
{file = "pdoc-11.0.0.tar.gz", hash = "sha256:722b4ff662574033de50b3061e55a81e5b0b54ff460f52f0c8a4abc25f08ce5b"}, {file = "pdoc-11.0.0.tar.gz", hash = "sha256:722b4ff662574033de50b3061e55a81e5b0b54ff460f52f0c8a4abc25f08ce5b"},
] ]
pexpect = [
{file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
{file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
]
pickleshare = [
{file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
]
pluggy = [ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
] ]
prompt-toolkit = [
{file = "prompt_toolkit-3.0.29-py3-none-any.whl", hash = "sha256:62291dad495e665fca0bda814e342c69952086afb0f4094d0893d357e5c78752"},
{file = "prompt_toolkit-3.0.29.tar.gz", hash = "sha256:bd640f60e8cecd74f0dc249713d433ace2ddc62b65ee07f96d358e0b152b6ea7"},
]
protobuf = [ protobuf = [
{file = "protobuf-3.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9d0f3aca8ca51c8b5e204ab92bd8afdb2a8e3df46bd0ce0bd39065d79aabcaa4"}, {file = "protobuf-3.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9d0f3aca8ca51c8b5e204ab92bd8afdb2a8e3df46bd0ce0bd39065d79aabcaa4"},
{file = "protobuf-3.20.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:001c2160c03b6349c04de39cf1a58e342750da3632f6978a1634a3dcca1ec10e"}, {file = "protobuf-3.20.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:001c2160c03b6349c04de39cf1a58e342750da3632f6978a1634a3dcca1ec10e"},
@ -1388,14 +955,6 @@ psutil = [
{file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
{file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
] ]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
pure-eval = [
{file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
{file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
]
py = [ py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
@ -1404,10 +963,6 @@ pycparser = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
] ]
pyflakes = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
pygments = [ pygments = [
{file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"},
{file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"},
@ -1487,9 +1042,6 @@ pytest-xprocess = [
{file = "pytest-xprocess-0.18.1.tar.gz", hash = "sha256:fd9f30ed1584b5833bc34494748adf0fb9de3ca7bacc4e88ad71989c21cba266"}, {file = "pytest-xprocess-0.18.1.tar.gz", hash = "sha256:fd9f30ed1584b5833bc34494748adf0fb9de3ca7bacc4e88ad71989c21cba266"},
{file = "pytest_xprocess-0.18.1-py3-none-any.whl", hash = "sha256:6f2aba817d842518d9d9dfb7e9adfe2a6e354a4359f4166bef0822ef4be1c9db"}, {file = "pytest_xprocess-0.18.1-py3-none-any.whl", hash = "sha256:6f2aba817d842518d9d9dfb7e9adfe2a6e354a4359f4166bef0822ef4be1c9db"},
] ]
pytools = [
{file = "pytools-2022.1.4.tar.gz", hash = "sha256:ae25f7c9b196fcd0d15e53bfe05236fe7bc5efd923810fbaeeee1a4bc4b6764a"},
]
requests = [ requests = [
{file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
{file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
@ -1510,10 +1062,6 @@ solana = [
{file = "solana-0.23.2-py3-none-any.whl", hash = "sha256:6c43a6013bc0b5358cafd94a4fa091d30f7917fe578960329a8977b9dd1b21a5"}, {file = "solana-0.23.2-py3-none-any.whl", hash = "sha256:6c43a6013bc0b5358cafd94a4fa091d30f7917fe578960329a8977b9dd1b21a5"},
{file = "solana-0.23.2.tar.gz", hash = "sha256:57c98be97d944440146780a4e175c0dbf50a988d75f05d81b5083620bf4331b0"}, {file = "solana-0.23.2.tar.gz", hash = "sha256:57c98be97d944440146780a4e175c0dbf50a988d75f05d81b5083620bf4331b0"},
] ]
stack-data = [
{file = "stack_data-0.2.0-py3-none-any.whl", hash = "sha256:999762f9c3132308789affa03e9271bbbe947bf78311851f4d485d8402ed858e"},
{file = "stack_data-0.2.0.tar.gz", hash = "sha256:45692d41bd633a9503a5195552df22b583caf16f0b27c4e58c98d88c8b648e12"},
]
sumtypes = [ sumtypes = [
{file = "sumtypes-0.1a6-py2.py3-none-any.whl", hash = "sha256:3e9d71322dd927d25d935072f8be7daec655ea292fd392359a5bb2c1e53dfdc3"}, {file = "sumtypes-0.1a6-py2.py3-none-any.whl", hash = "sha256:3e9d71322dd927d25d935072f8be7daec655ea292fd392359a5bb2c1e53dfdc3"},
{file = "sumtypes-0.1a6.tar.gz", hash = "sha256:1a6ff095e06a1885f340ddab803e0f38e3f9bed81f9090164ca9682e04e96b43"}, {file = "sumtypes-0.1a6.tar.gz", hash = "sha256:1a6ff095e06a1885f340ddab803e0f38e3f9bed81f9090164ca9682e04e96b43"},
@ -1522,18 +1070,10 @@ toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
] ]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
toolz = [ toolz = [
{file = "toolz-0.11.2-py3-none-any.whl", hash = "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f"}, {file = "toolz-0.11.2-py3-none-any.whl", hash = "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f"},
{file = "toolz-0.11.2.tar.gz", hash = "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33"}, {file = "toolz-0.11.2.tar.gz", hash = "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33"},
] ]
traitlets = [
{file = "traitlets-5.2.2.post1-py3-none-any.whl", hash = "sha256:1530d04badddc6a73d50b7ee34667d4b96914da352109117b4280cb56523a51b"},
{file = "traitlets-5.2.2.post1.tar.gz", hash = "sha256:74803a1baa59af70f023671d86d5c7a834c931186df26d50d362ee6a1ff021fd"},
]
typer = [ typer = [
{file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"}, {file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"},
{file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"}, {file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"},
@ -1551,10 +1091,6 @@ urllib3 = [
{file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
{file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
] ]
wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
websockets = [ websockets = [
{file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"}, {file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"},
{file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"}, {file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"},

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "switchboardpy" name = "switchboardpy"
version = "0.0.5" version = "0.0.7"
description = "Switchboard V2 API" description = "Switchboard V2 API"
repository = "https://github.com/switchboard-xyz/switchboard-v2/tree/main/libraries/py" repository = "https://github.com/switchboard-xyz/switchboard-v2/tree/main/libraries/py"
homepage = "https://docs.switchboard.xyz" homepage = "https://docs.switchboard.xyz"
@ -9,7 +9,7 @@ license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"
anchorpy = {extras = ["cli"], version = "^0.9.1"} anchorpy = "^0.9.2"
anyio = "3.4.0" anyio = "3.4.0"
apischema = "0.16.6" apischema = "0.16.6"
attrs = "21.2.0" attrs = "21.2.0"
@ -55,6 +55,7 @@ urllib3 = "1.26.7"
websockets = "10.1" websockets = "10.1"
zstandard = ">=0.17.0" zstandard = ">=0.17.0"
typer = "^0.4.1" typer = "^0.4.1"
pytest-xprocess = "0.18.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "6.2.5" pytest = "6.2.5"

View File

@ -6,7 +6,7 @@ from switchboardpy.aggregator import (
AggregatorInitParams, AggregatorInitParams,
AggregatorOpenRoundParams, AggregatorOpenRoundParams,
AggregatorSaveResultParams, AggregatorSaveResultParams,
AggregatorSetHistoryBufferParams AggregatorSetHistoryBufferParams,
) )
from switchboardpy.compiled import OracleJob from switchboardpy.compiled import OracleJob
from switchboardpy.common import SBV2_DEVNET_PID, AccountParams, SwitchboardDecimal from switchboardpy.common import SBV2_DEVNET_PID, AccountParams, SwitchboardDecimal
@ -49,5 +49,7 @@ __all__ = [
"ProgramStateAccount", "ProgramStateAccount",
"ProgramInitParams", "ProgramInitParams",
"VaultTransferParams", "VaultTransferParams",
"SwitchboardDecimal" "SwitchboardDecimal",
"readRawVarint32",
"readDelimitedFrom"
] ]

View File

@ -11,14 +11,17 @@ from typing import Optional, Any, NamedTuple
from solana.keypair import Keypair from solana.keypair import Keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from spl.token.async_client import AsyncToken
from spl.token.constants import TOKEN_PROGRAM_ID
from solana.transaction import TransactionSignature from solana.transaction import TransactionSignature
from spl.token.instructions import get_associated_token_address from solana.rpc.commitment import Confirmed
from solana.system_program import CreateAccountParams, create_account from solana.system_program import CreateAccountParams, create_account
from spl.token.async_client import AsyncToken
from spl.token.constants import TOKEN_PROGRAM_ID
from spl.token.instructions import get_associated_token_address
from switchboardpy.compiled import OracleJob from switchboardpy.compiled import OracleJob
from switchboardpy.common import AccountParams, SwitchboardDecimal from switchboardpy.common import AccountParams, SwitchboardDecimal, parseOracleJob
from switchboardpy.program import ProgramStateAccount from switchboardpy.program import ProgramStateAccount
from switchboardpy.oraclequeue import OracleQueueAccount from switchboardpy.oraclequeue import OracleQueueAccount
from switchboardpy.oracle import OracleAccount from switchboardpy.oracle import OracleAccount
@ -26,6 +29,8 @@ from switchboardpy.job import JobAccount
from switchboardpy.lease import LeaseAccount from switchboardpy.lease import LeaseAccount
from switchboardpy.permission import PermissionAccount from switchboardpy.permission import PermissionAccount
from .generated.accounts import AggregatorAccountData
# Parameters for which oracles must submit for responding to update requests. # Parameters for which oracles must submit for responding to update requests.
@dataclass @dataclass
class AggregatorSaveResultParams: class AggregatorSaveResultParams:
@ -64,7 +69,6 @@ class AggregatorSaveResultParams:
oracles: list[Any] oracles: list[Any]
# Parameters for creating and setting a history buffer # Parameters for creating and setting a history buffer
@dataclass @dataclass
class AggregatorSetHistoryBufferParams: class AggregatorSetHistoryBufferParams:
@ -75,6 +79,16 @@ class AggregatorSetHistoryBufferParams:
"""Authority keypair for the aggregator""" """Authority keypair for the aggregator"""
authority: Keypair = None authority: Keypair = None
# Parameters for creating and setting a history buffer
@dataclass
class AggregatorSetUpdateIntervalParams:
"""Seconds between updates"""
new_interval: int
"""Authority keypair for the aggregator"""
authority: Keypair = None
# Parameters required to open an aggregator round # Parameters required to open an aggregator round
@dataclass @dataclass
class AggregatorOpenRoundParams: class AggregatorOpenRoundParams:
@ -85,6 +99,80 @@ class AggregatorOpenRoundParams:
"""The token wallet which will receive rewards for calling update on this feed.""" """The token wallet which will receive rewards for calling update on this feed."""
payout_wallet: PublicKey payout_wallet: PublicKey
"""
Data feeds on a crank are ordered by their next available update time with some
level of jitter to mitigate oracles being assigned to the same update request upon
each iteration of the queue, which makes them susceptible to a malicous oracle.
"""
jitter: int = None
# Result of returning loadedJobs
@dataclass
class AggregatorLoadedJob:
"""The oracle queue from which oracles are assigned this update."""
job: OracleJob
"""Public Key of a given job"""
public_key: PublicKey
"""Job account data"""
account: Any
# Parameters required to set min jobs for Aggregator
@dataclass
class AggregatorSetMinJobsParams:
"""The min number of jobs required"""
min_job_results: int
"""The feed authority."""
authority: Keypair = None
# Parameters required to set batch size for Aggregator
@dataclass
class AggregatorSetBatchSizeParams:
"""The batch size."""
batch_size: int
"""The feed authority."""
authority: Keypair = None
# Parameters required to set min oracles for Aggregator
@dataclass
class AggregatorSetMinOraclesParams:
"""The min results required"""
min_oracle_results: int
"""The feed authority."""
authority: Keypair = None
# Parameters required to set min oracles for Aggregator
@dataclass
class AggregatorSetQueueParams:
"""The min results required"""
queue_account: OracleQueueAccount
"""The feed authority."""
authority: Keypair = None
# Parameters required to set min oracles for Aggregator
@dataclass
class AggregatorSetVarianceThresholdParams:
"""The % change needed to trigger an update"""
threshold: Decimal
"""The feed authority."""
authority: Keypair = None
# Init Params for Aggregators # Init Params for Aggregators
@dataclass @dataclass
@ -149,6 +237,9 @@ class AggregatorInitParams:
""" """
authority: PublicKey = None authority: PublicKey = None
"""Disable automatic updates"""
disable_crank: bool = None
@dataclass @dataclass
class AggregatorHistoryRow: class AggregatorHistoryRow:
@ -228,9 +319,8 @@ class AggregatorAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
aggregator = await self.program.account["AggregatorAccountData"].fetch(self.public_key) return await AggregatorAccountData.fetch(self.program.provider.connection, self.public_key)
aggregator.ebuf = None
return aggregator
""" """
Get AggregatorAccount historical data Get AggregatorAccount historical data
@ -350,11 +440,14 @@ class AggregatorAccount:
last_timestamp = aggregator.latest_confirmed_round.round_open_timestamp last_timestamp = aggregator.latest_confirmed_round.round_open_timestamp
if last_timestamp + force_report_period < timestamp: if last_timestamp + force_report_period < timestamp:
return True return True
if value < latest_result - variance_threshold: diff = latest_result / value
if abs(diff) > 1:
diff = value / latest_result
if diff < 0:
return True return True
if value > latest_result + variance_threshold:
return True change_percentage = 1 - diff * 100
return False return change_percentage > variance_threshold
""" """
Get the individual oracle results of the latest confirmed round. Get the individual oracle results of the latest confirmed round.
@ -410,7 +503,7 @@ class AggregatorAccount:
aggregator (Any): Optional aggregator aggregator (Any): Optional aggregator
Returns: Returns:
jobs (list[OracleJob]): latest feed timestamp as hex string jobs (list[{ "job": OracleJob, "public_key": PublicKey, "account": JobAccountData }])
Raises: Raises:
ValueError: Failed to load feed jobs. ValueError: Failed to load feed jobs.
@ -420,12 +513,12 @@ class AggregatorAccount:
async def load_jobs(self, aggregator: Optional[Any] = None) -> Decimal: async def load_jobs(self, aggregator: Optional[Any] = None) -> Decimal:
coder = anchorpy.AccountsCoder(self.program.idl) coder = anchorpy.AccountsCoder(self.program.idl)
aggregator = aggregator if aggregator else await self.load_data() aggregator = aggregator if aggregator else await self.load_data()
job_accounts_raw = await anchorpy.utils.rpc.get_multiple_accounts(self.program.provider, aggregator.job_pubkeys_data)[:aggregator.job_pubkeys_size] job_accounts_raw = await anchorpy.utils.rpc.get_multiple_accounts(self.program.provider.connection, aggregator.job_pubkeys_data[:aggregator.job_pubkeys_size], 10, Confirmed)
if not job_accounts_raw: if not job_accounts_raw:
raise ValueError('Failed to load feed jobs.') raise ValueError('Failed to load feed jobs.')
# Deserialize OracleJob objects from each decoded JobAccountData # Deserialize OracleJob objects from each decoded JobAccountData
return [OracleJob.ParseFromString(coder.decode(job)) for job in job_accounts_raw] return [AggregatorLoadedJob(parseOracleJob(coder.decode(job.account.data).data), job.pubkey, coder.decode(job.account.data)) for job in job_accounts_raw]
""" """
Load all job hashes for each job stored in this aggregator Load all job hashes for each job stored in this aggregator
@ -443,12 +536,12 @@ class AggregatorAccount:
async def load_hashes(self, aggregator: Optional[Any] = None) -> Decimal: async def load_hashes(self, aggregator: Optional[Any] = None) -> Decimal:
coder = anchorpy.AccountsCoder(self.program.idl) coder = anchorpy.AccountsCoder(self.program.idl)
aggregator = aggregator if aggregator else await self.loadData() aggregator = aggregator if aggregator else await self.loadData()
job_accounts_raw = await anchorpy.utils.rpc.get_multiple_accounts(self.program.provider, aggregator.job_pubkeys_data)[:aggregator.job_pubkeys_size] job_accounts_raw = await anchorpy.utils.rpc.get_multiple_accounts(self.program.provider.connection, aggregator.job_pubkeys_data[:aggregator.job_pubkeys_size])
if not job_accounts_raw: if not job_accounts_raw:
raise ValueError('Failed to load feed jobs.') raise ValueError('Failed to load feed jobs.')
# get hashes from each decoded JobAccountData # get hashes from each decoded JobAccountData
return [coder.decode(job).hash for job in job_accounts_raw] return [coder.decode(job.account.data).hash for job in job_accounts_raw]
""" """
@ -479,7 +572,7 @@ class AggregatorAccount:
state = await state_account.load_data() state = await state_account.load_data()
response = await program.provider.connection.get_minimum_balance_for_rent_exemption(size) response = await program.provider.connection.get_minimum_balance_for_rent_exemption(size)
lamports = response["result"] lamports = response["result"]
zero_decimal = program.type['SwitchboardDecimal'](0, 0) zero_decimal = SwitchboardDecimal(0, 0).as_proper_sbd(program)
await program.rpc["aggregator_init"]( await program.rpc["aggregator_init"](
{ {
@ -493,7 +586,8 @@ class AggregatorAccount:
"force_report_period": aggregator_init_params.force_report_period or 0, "force_report_period": aggregator_init_params.force_report_period or 0,
"expiration": aggregator_init_params.expiration or 0, "expiration": aggregator_init_params.expiration or 0,
"state_bump": state_bump, "state_bump": state_bump,
"start_after": aggregator_init_params.start_after, "disable_crank": aggregator_init_params.disable_crank or False,
"start_after": aggregator_init_params.start_after or 0,
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
@ -561,16 +655,23 @@ class AggregatorAccount:
) )
) )
# TODO: Verify this function """
Open round on aggregator to get an update
Args:
program (anchorpy.Program): Switchboard program representation holding connection and IDL
params (AggregatorOpenRoundParams)
Returns:
TransactionSignature
"""
async def open_round(self, params: AggregatorOpenRoundParams): async def open_round(self, params: AggregatorOpenRoundParams):
program = self.program program = self.program
authority = params.authority or self.keypair
state_account, state_bump = ProgramStateAccount.from_seed(program) state_account, state_bump = ProgramStateAccount.from_seed(program)
mint = await state_account.get_token_mint()
queue = await params.oracle_queue_account.load_data() queue = await params.oracle_queue_account.load_data()
lease_account, lease_bump = LeaseAccount.from_seed( lease_account, lease_bump = LeaseAccount.from_seed(
self.program, self.program,
queue_account, params.oracle_queue_account,
self self
) )
lease = await lease_account.load_data() lease = await lease_account.load_data()
@ -580,17 +681,18 @@ class AggregatorAccount:
params.oracle_queue_account.public_key, params.oracle_queue_account.public_key,
self.public_key self.public_key
) )
await program.rpc["aggregator_open_round"]( return await program.rpc["aggregator_open_round"](
{ {
"state_bump": state_bump, "state_bump": state_bump,
"lease_bump": lease_bump, "lease_bump": lease_bump,
"permission_bump": permission_bump, "permission_bump": permission_bump,
"jitter": params.jitter or 0
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
"aggregator": self.public_key, "aggregator": self.public_key,
"lease": self.public_key, "lease": lease_account.public_key,
"oracle_queue": self.public_key, "oracle_queue": params.oracle_queue_account.public_key,
"queue_authority": queue.authority, "queue_authority": queue.authority,
"permission": permission_account.public_key, "permission": permission_account.public_key,
"escrow": lease.escrow, "escrow": lease.escrow,
@ -598,9 +700,58 @@ class AggregatorAccount:
"payout_wallet": params.payout_wallet, "payout_wallet": params.payout_wallet,
"token_program": TOKEN_PROGRAM_ID, "token_program": TOKEN_PROGRAM_ID,
"data_buffer": queue.data_buffer, "data_buffer": queue.data_buffer,
"mint": mint.public_key, "mint": (await params.oracle_queue_account.load_mint()).pubkey,
}, },
signers=[self.keypair] )
)
"""
Set min jobs sets the min jobs parameter. This is a suggestion to oracles
of the number of jobs that must resolve for a job to be considered valid.
Args:
params (AggregatorSetMinJobsParams): parameters pecifying the min jobs that must respond
Returns:
TransactionSignature
"""
async def set_min_jobs(self, params: AggregatorSetMinJobsParams):
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_min_jobs'](
{
"min_job_results": params.min_job_results
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
)
)
"""
Set min oracles sets the min oracles parameter. This will determine how many oracles need to come back with a
valid response for a result to be accepted.
Args:
params (AggregatorSetMinOraclesParams): parameters pecifying the min jobs that must respond
Returns:
TransactionSignature
"""
async def set_min_jobs(self, params: AggregatorSetMinJobsParams):
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_min_jobs'](
{
"min_job_results": params.min_job_results
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
) )
) )
@ -613,12 +764,12 @@ class AggregatorAccount:
Returns: Returns:
TransactionSignature TransactionSignature
""" """
async def add_job(self, job: JobAccount, authority: Optional[Keypair] = None) -> TransactionSignature: async def add_job(self, job: JobAccount, weight: int = 0, authority: Optional[Keypair] = None) -> TransactionSignature:
authority = authority or self.keypair authority = authority or self.keypair
return await self.program.rpc['aggregator_add_job']( return await self.program.rpc['aggregator_add_job'](
{ {
"params": None "weight": weight
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
@ -630,6 +781,102 @@ class AggregatorAccount:
) )
) )
"""
RPC Set batch size / the number of oracles that'll respond to updates
Args:
params (AggregatorSetBatchSizeParams)
Returns:
TransactionSignature
"""
async def set_batch_size(self, params: AggregatorSetBatchSizeParams) -> TransactionSignature:
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_batch_size'](
{
"batch_size": params.batch_size
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
)
)
"""
RPC set variance threshold (only write updates when response is > variance threshold %)
Args:
params (AggregatorSetVarianceThresholdParams)
Returns:
TransactionSignature
"""
async def set_variance_threshold(self, params: AggregatorSetVarianceThresholdParams) -> TransactionSignature:
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_variance_threshold'](
{
"variance_threshold": SwitchboardDecimal.from_decimal(params.threshold)
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
)
)
"""
RPC set min oracles
Args:
params (AggregatorSetMinOraclesParams)
Returns:
TransactionSignature
"""
async def set_min_oracles(self, params: AggregatorSetMinOraclesParams) -> TransactionSignature:
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_min_oracles'](
{
"min_oracle_results": params.min_oracle_results
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
)
)
"""
RPC set update interval
Args:
params (AggregatorSetUpdateIntervalParams)
Returns:
TransactionSignature
"""
async def set_update_interval(self, params: AggregatorSetUpdateIntervalParams) -> TransactionSignature:
authority = authority or self.keypair
return await self.program.rpc['aggregator_set_update_interval'](
{
"new_interval": params.new_interval
},
ctx=anchorpy.Context(
accounts={
"aggregator": self.public_key,
"authority": authority.public_key,
},
signers=[authority]
)
)
""" """
Prevent new jobs from being added to the feed. Prevent new jobs from being added to the feed.
@ -802,7 +1049,8 @@ class AggregatorAccount:
"escrow": escrow, "escrow": escrow,
"token_program": TOKEN_PROGRAM_ID, "token_program": TOKEN_PROGRAM_ID,
"program_state": program_state_account.public_key, "program_state": program_state_account.public_key,
"history_buffer": history_buffer "history_buffer": history_buffer,
"mint": params.token_mint
}, },
remaining_accounts=[{"is_signer": False, "is_writable": True, "pubkey": pubkey} for pubkey in remaining_accounts] remaining_accounts=[{"is_signer": False, "is_writable": True, "pubkey": pubkey} for pubkey in remaining_accounts]
) )

View File

@ -1,17 +1,70 @@
import io
import six
import anchorpy import anchorpy
from dataclasses import dataclass from dataclasses import dataclass
from functools import reduce from functools import reduce
from typing import Any from typing import Any, Type
from decimal import Decimal from decimal import Decimal
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.keypair import Keypair from solana.keypair import Keypair
from google.protobuf.internal import decoder
from switchboardpy.compiled import OracleJob
# Devnet Program ID. # Devnet Program ID.
SBV2_DEVNET_PID = PublicKey( SBV2_DEVNET_PID = PublicKey(
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG' '2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
) )
# Mainnet-Beta Program ID.
SBV2_MAINNET_PID = PublicKey(
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
)
# https://stackoverflow.com/a/34539706
def readRawVarint32(stream: io.BytesIO):
mask = 0x80 # (1 << 7)
raw_varint32 = []
while 1:
b = stream.read(1)
#eof
if b == "":
break
raw_varint32.append(b)
if not (ord(b) & mask):
#we found a byte starting with a 0, which means it's the last byte of this varint
break
return raw_varint32
def getSize(raw_varint32):
result = 0
shift = 0
b = six.indexbytes(raw_varint32, 0)
result |= ((ord(b) & 0x7f) << shift)
return result
def readDelimitedFrom(MessageType: Type[OracleJob], stream: io.BytesIO):
raw_varint32 = readRawVarint32(stream)
message = None
if raw_varint32:
size = getSize(raw_varint32)
data = stream.read(size)
if len(data) < size:
raise Exception("Unexpected end of file")
message = MessageType()
message.ParseFromString(data)
return message
# take OracleJob data and return bytes
def parseOracleJob(data: bytes):
dataStream = io.BytesIO(data)
return readDelimitedFrom(OracleJob, dataStream);
# Input parameters for constructing wrapped representations of Switchboard accounts. # Input parameters for constructing wrapped representations of Switchboard accounts.
@dataclass @dataclass
class AccountParams: class AccountParams:

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,8 @@ from solana.system_program import CreateAccountParams, create_account
from switchboardpy.program import ProgramStateAccount from switchboardpy.program import ProgramStateAccount
from .generated.accounts import CrankAccountData
# Parameters for initializing a CrankAccount # Parameters for initializing a CrankAccount
@dataclass @dataclass
class CrankInitParams: class CrankInitParams:
@ -129,9 +131,8 @@ class CrankAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
crank = await self.program.account["CrankAccountData"].fetch(self.public_key) return await CrankAccountData.fetch(self.program.provider.connection, self.public_key)
crank.ebuf = None
return crank
""" """
Create and initialize the CrankAccount. Create and initialize the CrankAccount.

View File

@ -1,3 +1,7 @@
import os
from solana.publickey import PublicKey from solana.publickey import PublicKey
PROGRAM_ID = PublicKey("SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f") os.environ.setdefault('SWITCHBOARD_PID', '2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG')
PROGRAM_ID = PublicKey(os.environ['SWITCHBOARD_PID'] or "SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f")

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class AccountMetaBorshJSON(typing.TypedDict): class AccountMetaBorshJSON(typing.TypedDict):

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class AccountMetaZCJSON(typing.TypedDict): class AccountMetaZCJSON(typing.TypedDict):

View File

@ -7,6 +7,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class AggregatorRoundJSON(typing.TypedDict): class AggregatorRoundJSON(typing.TypedDict):

View File

@ -7,6 +7,7 @@ from dataclasses import dataclass
from construct import Container, Construct from construct import Container, Construct
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class CallbackJSON(typing.TypedDict): class CallbackJSON(typing.TypedDict):

View File

@ -7,6 +7,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class CallbackZCJSON(typing.TypedDict): class CallbackZCJSON(typing.TypedDict):

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class CrankRowJSON(typing.TypedDict): class CrankRowJSON(typing.TypedDict):

View File

@ -3,6 +3,8 @@ import typing
from dataclasses import dataclass from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
from anchorpy.borsh_extension import BorshPubkey
import borsh_construct as borsh import borsh_construct as borsh

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class ProgramConfigParamsJSON(typing.TypedDict): class ProgramConfigParamsJSON(typing.TypedDict):

View File

@ -13,6 +13,7 @@ from dataclasses import dataclass
from construct import Container from construct import Container
from solana.publickey import PublicKey from solana.publickey import PublicKey
import borsh_construct as borsh import borsh_construct as borsh
from anchorpy.borsh_extension import BorshPubkey
class VrfBuilderJSON(typing.TypedDict): class VrfBuilderJSON(typing.TypedDict):

View File

@ -1,3 +1,4 @@
import io
import anchorpy import anchorpy
from dataclasses import dataclass from dataclasses import dataclass
@ -6,9 +7,12 @@ from solana.publickey import PublicKey
from solana.system_program import CreateAccountParams, create_account from solana.system_program import CreateAccountParams, create_account
from switchboardpy.compiled import OracleJob from switchboardpy.compiled import OracleJob
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams, parseOracleJob
from switchboardpy.program import ProgramStateAccount from switchboardpy.program import ProgramStateAccount
from .generated.accounts import JobAccountData
# Parameters for initializing a JobAccount # Parameters for initializing a JobAccount
@dataclass @dataclass
class JobInitParams: class JobInitParams:
@ -30,9 +34,8 @@ class JobInitParams:
""" """
An optional wallet for receiving kickbacks from job usage in feeds. An optional wallet for receiving kickbacks from job usage in feeds.
Defaults to token vault.
""" """
author_wallet: PublicKey = None authority: PublicKey = None
class JobAccount: class JobAccount:
""" A Switchboard account representing a job for an oracle to perform, stored as """ A Switchboard account representing a job for an oracle to perform, stored as
@ -68,9 +71,8 @@ class JobAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
job = await self.program.account["JobAccountData"].fetch(self.public_key) return await JobAccountData.fetch(self.program.provider.connection, self.public_key)
job.ebuf = None
return job
""" """
Load and parse the protobuf from the raw buffer stored in the JobAccount. Load and parse the protobuf from the raw buffer stored in the JobAccount.
@ -83,8 +85,8 @@ class JobAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_job(self): async def load_job(self):
job = await self.load_job() job = await self.load_data()
return OracleJob.ParseFromString(job.data) return parseOracleJob(job.data);
""" """
Load and parse JobAccount data based on the program IDL from a buffer. Load and parse JobAccount data based on the program IDL from a buffer.
@ -132,7 +134,7 @@ class JobAccount:
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
"job": job_account.public_key, "job": job_account.public_key,
"author_wallet": params.author_wallet or state.token_vault, "authority": params.authority or state.token_vault,
"program_state": state_account.public_key "program_state": state_account.public_key
}, },
signers=[job_account], signers=[job_account],

View File

@ -5,10 +5,11 @@ import anchorpy
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dataclasses import dataclass from dataclasses import dataclass
from decimal import Decimal
from solana import publickey, system_program from solana import publickey, system_program
from solana.keypair import Keypair from solana.keypair import Keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.transaction import AccountMeta
from spl.token.constants import ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID from spl.token.constants import ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID
from spl.token.instructions import get_associated_token_address from spl.token.instructions import get_associated_token_address
@ -16,6 +17,8 @@ from switchboardpy.oraclequeue import OracleQueueAccount
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams
from switchboardpy.program import ProgramStateAccount from switchboardpy.program import ProgramStateAccount
from .generated.accounts import LeaseAccountData
if TYPE_CHECKING: if TYPE_CHECKING:
from switchboardpy.aggregator import AggregatorAccount from switchboardpy.aggregator import AggregatorAccount
@ -113,9 +116,8 @@ class LeaseAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
lease = await self.program.account["LeaseAccountData"].fetch(self.public_key) return await LeaseAccountData.fetch(self.program.provider.connection, self.public_key)
lease.ebuf = None
return lease
""" """
Loads a LeaseAccount from the expected PDA seed format Loads a LeaseAccount from the expected PDA seed format
@ -161,13 +163,33 @@ class LeaseAccount:
params.aggregator_account params.aggregator_account
) )
job_account_data = await params.aggregator_account.load_jobs()
aggregator_account_data = await params.aggregator_account.load_data()
job_pubkeys: list[PublicKey] = aggregator_account_data.job_pubkeys_data[:aggregator_account_data.job_pubkeys_size]
job_wallets: list[PublicKey] = []
wallet_bumps: list[int] = []
for job in job_account_data:
authority = job.account.authority or PublicKey('11111111111111111111111111111111')
pubkey, bump = publickey.PublicKey.find_program_address(
[
bytes(authority),
bytes(TOKEN_PROGRAM_ID),
bytes(switch_token_mint.pubkey),
],
ASSOCIATED_TOKEN_PROGRAM_ID
)
job_wallets.append(pubkey)
wallet_bumps.append(bump)
escrow = await switch_token_mint.create_associated_token_account(lease_account.public_key, skip_confirmation=False) escrow = await switch_token_mint.create_associated_token_account(lease_account.public_key, skip_confirmation=False)
await program.rpc["lease_init"]( await program.rpc["lease_init"](
{ {
"load_amount": params.load_amount, "load_amount": params.load_amount,
"state_bump": state_bump, "state_bump": state_bump,
"lease_bump": lease_bump, "lease_bump": lease_bump,
"withdraw_authority": params.withdraw_authority or PublicKey('11111111111111111111111111111111') "withdraw_authority": params.withdraw_authority or PublicKey('11111111111111111111111111111111'),
"wallet_bumps": bytes(wallet_bumps)
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
@ -181,12 +203,25 @@ class LeaseAccount:
"token_program": TOKEN_PROGRAM_ID, "token_program": TOKEN_PROGRAM_ID,
"escrow": escrow, "escrow": escrow,
"owner": params.funder_authority.public_key, "owner": params.funder_authority.public_key,
"mint": switch_token_mint.pubkey
}, },
signers=[params.funder_authority] signers=[params.funder_authority],
remaining_accounts=[AccountMeta(is_signer=False, is_writable=True, pubkey=x) for x in [*job_pubkeys, *job_wallets]]
) )
) )
return LeaseAccount(AccountParams(program=program, public_key=lease_account.public_key)) return LeaseAccount(AccountParams(program=program, public_key=lease_account.public_key))
"""
Get lease balance
Args:
Returns:
int balance
"""
async def get_balance(self):
lease = self.load_data()
return await self.program.provider.connection.get_balance(lease.escrow)
""" """
Adds fund to a LeaseAccount. Note that funds can always be withdrawn by Adds fund to a LeaseAccount. Note that funds can always be withdrawn by
the withdraw authority if one was set on lease initialization. the withdraw authority if one was set on lease initialization.
@ -205,17 +240,37 @@ class LeaseAccount:
queue = lease.queue queue = lease.queue
aggregator = lease.aggregator aggregator = lease.aggregator
program_state_account, state_bump = ProgramStateAccount.from_seed(program) program_state_account, state_bump = ProgramStateAccount.from_seed(program)
switch_token_mint = await program_state_account.get_token_mint() queue_account = OracleQueueAccount(AccountParams(program=program, public_key=queue))
switch_token_mint = await queue_account.load_mint()
lease_account, lease_bump = LeaseAccount.from_seed( lease_account, lease_bump = LeaseAccount.from_seed(
program, program,
OracleQueueAccount(AccountParams(program=program, public_key=queue)), OracleQueueAccount(AccountParams(program=program, public_key=queue)),
AggregatorAccount(AccountParams(program=program, public_key=aggregator)) AggregatorAccount(AccountParams(program=program, public_key=aggregator))
) )
job_account_data = await aggregator.load_jobs()
aggregator_account_data = await aggregator.load_data()
job_pubkeys: list[PublicKey] = aggregator_account_data.job_pubkeys_data[:aggregator_account_data.job_pubkeys_size]
job_wallets: list[PublicKey] = []
wallet_bumps: list[int] = []
for job in job_account_data:
authority = job.account.authority or PublicKey('11111111111111111111111111111111')
pubkey, bump = publickey.PublicKey.find_program_address(
[
bytes(authority),
bytes(TOKEN_PROGRAM_ID),
bytes(switch_token_mint.pubkey),
],
ASSOCIATED_TOKEN_PROGRAM_ID
)
job_wallets.append(pubkey)
wallet_bumps.append(bump)
return await program.rpc["lease_extend"]( return await program.rpc["lease_extend"](
{ {
"load_amount": params.load_amount, "load_amount": params.load_amount,
"state_bump": state_bump, "state_bump": state_bump,
"lease_bump": lease_bump "lease_bump": lease_bump,
"wallet_bumps": bytes(wallet_bumps)
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
@ -226,9 +281,11 @@ class LeaseAccount:
"owner": params.funder_authority.public_key, "owner": params.funder_authority.public_key,
"token_program": TOKEN_PROGRAM_ID, "token_program": TOKEN_PROGRAM_ID,
"escrow": escrow, "escrow": escrow,
"program_state": program_state_account.public_key "program_state": program_state_account.public_key,
"mint": switch_token_mint.pubkey
}, },
signers=[params.funder_authority] signers=[params.funder_authority],
remaining_accounts=[AccountMeta(is_signer=False, is_writable=True, pubkey=x) for x in [*job_pubkeys, *job_wallets]]
) )
) )
@ -252,6 +309,8 @@ class LeaseAccount:
queue = lease.queue queue = lease.queue
aggregator = lease.aggregator aggregator = lease.aggregator
program_state_account, state_bump = ProgramStateAccount.from_seed(program) program_state_account, state_bump = ProgramStateAccount.from_seed(program)
queue_account = OracleQueueAccount(AccountParams(program=program, public_key=queue))
switch_token_mint = await queue_account.load_mint()
lease_account, lease_bump = LeaseAccount.from_seed( lease_account, lease_bump = LeaseAccount.from_seed(
program, program,
OracleQueueAccount(AccountParams(program=program, public_key=queue)), OracleQueueAccount(AccountParams(program=program, public_key=queue)),
@ -273,6 +332,7 @@ class LeaseAccount:
"withdraw_account": params.withdraw_wallet, "withdraw_account": params.withdraw_wallet,
"token_program": TOKEN_PROGRAM_ID, "token_program": TOKEN_PROGRAM_ID,
"program_state": program_state_account.public_key, "program_state": program_state_account.public_key,
"mint": switch_token_mint.pubkey
}, },
signers=[params.withdraw_authority] signers=[params.withdraw_authority]
) )

View File

@ -14,6 +14,7 @@ from switchboardpy.program import ProgramStateAccount
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams
from switchboardpy.oraclequeue import OracleQueueAccount from switchboardpy.oraclequeue import OracleQueueAccount
from .generated.accounts import OracleAccountData
# Parameters for an OracleInit request # Parameters for an OracleInit request
@dataclass @dataclass
@ -85,9 +86,8 @@ class OracleAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
oracle = await self.program.account["OracleAccountData"].fetch(self.public_key) return await OracleAccountData.fetch(self.program.provider.connection, self.public_key)
oracle.ebuf = None
return oracle
""" """
Loads a OracleAccount from the expected PDA seed format Loads a OracleAccount from the expected PDA seed format

View File

@ -3,19 +3,27 @@ import anchorpy
from dataclasses import dataclass from dataclasses import dataclass
from decimal import Decimal from decimal import Decimal
from spl.token.async_client import AsyncToken
from spl.token.constants import TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT
from solana import system_program from solana import system_program
from solana import keypair from solana import keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.keypair import Keypair from solana.keypair import Keypair
from solana.system_program import CreateAccountParams, create_account from solana.system_program import CreateAccountParams, create_account
from switchboardpy.common import SwitchboardDecimal from switchboardpy.common import SwitchboardDecimal
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams
from .generated.accounts import OracleQueueAccountData
# Parameters for initializing OracleQueueAccount # Parameters for initializing OracleQueueAccount
@dataclass @dataclass
class OracleQueueInitParams: class OracleQueueInitParams:
"""Mint for the oracle queue"""
mint: PublicKey
"""Rewards to provide oracles and round openers on this queue.""" """Rewards to provide oracles and round openers on this queue."""
reward: int reward: int
@ -74,6 +82,13 @@ class OracleQueueInitParams:
"""Buffer for queue metadata.""" """Buffer for queue metadata."""
metadata: bytes = None metadata: bytes = None
"""
Enabling this setting means data feeds do not need explicit permission
to request VRF proofs and verifications from this queue.
"""
unpermissioned_vrf: bool = None
class OracleQueueAccount: class OracleQueueAccount:
"""A Switchboard account representing a queue for distributing oracles to """A Switchboard account representing a queue for distributing oracles to
permitted data feeds. permitted data feeds.
@ -117,9 +132,22 @@ class OracleQueueAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
queue = await self.program.account["OracleQueueAccountData"].fetch(self.public_key) return await OracleQueueAccountData.fetch(self.program.provider.connection, self.public_key)
queue.ebuf = None
return queue """
Fetch the token mint for this queue
Args:
Returns:
AsyncToken
"""
async def load_mint(self) -> AsyncToken:
payer_keypair = Keypair.from_secret_key(self.program.provider.wallet.payer.secret_key)
queue = await self.load_data()
try:
mint = AsyncToken(self.program.provider.connection, queue.mint, TOKEN_PROGRAM_ID, payer_keypair)
return mint;
except AttributeError:
return AsyncToken(self.program.provider.connection, WRAPPED_SOL_MINT, TOKEN_PROGRAM_ID, payer_keypair)
""" """
Create and initialize the OracleQueueAccount Create and initialize the OracleQueueAccount
@ -154,7 +182,9 @@ class OracleQueueAccount:
"consecutive_oracle_failure_limit": params.consecutive_oracle_failure_limit or 1000, "consecutive_oracle_failure_limit": params.consecutive_oracle_failure_limit or 1000,
"minimum_delay_seconds": params.minimum_delay_seconds or 5, "minimum_delay_seconds": params.minimum_delay_seconds or 5,
"queue_size": params.queue_size or 0, "queue_size": params.queue_size or 0,
"unpermissioned_feeds": params.unpermissioned_feeds or False "unpermissioned_feeds": params.unpermissioned_feeds or False,
"unpermissioned_vrf": params.unpermissioned_feeds or False,
"enable_buffer_relayers": False
}, },
ctx=anchorpy.Context( ctx=anchorpy.Context(
accounts={ accounts={
@ -162,7 +192,8 @@ class OracleQueueAccount:
"authority": params.authority, "authority": params.authority,
"buffer": buffer.public_key, "buffer": buffer.public_key,
"system_program": system_program.SYS_PROGRAM_ID, "system_program": system_program.SYS_PROGRAM_ID,
"payer": program.provider.wallet.public_key "payer": program.provider.wallet.public_key,
"mint": params.mint
}, },
signers=[oracle_queue_account, buffer], signers=[oracle_queue_account, buffer],
pre_instructions=[ pre_instructions=[

View File

@ -8,6 +8,8 @@ from solana.keypair import Keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams
from .generated.accounts import PermissionAccountData
# Parameters for initializing PermissionAccount # Parameters for initializing PermissionAccount
@dataclass @dataclass
class PermissionInitParams: class PermissionInitParams:
@ -91,9 +93,8 @@ class PermissionAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
permission = await self.program.account["PermissionAccountData"].fetch(self.public_key) return await PermissionAccountData.fetch(self.program.provider.connection, self.public_key)
permission.ebuf = None
return permission
""" """
Get the size of a PermissionAccount on chain Get the size of a PermissionAccount on chain

View File

@ -12,11 +12,19 @@ from solana.publickey import PublicKey
from switchboardpy.common import AccountParams from switchboardpy.common import AccountParams
from .generated.accounts import SbState
# Devnet Program ID. # Devnet Program ID.
SBV2_DEVNET_PID = PublicKey( SBV2_DEVNET_PID = PublicKey(
'2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG' '2TfB33aLaneQb5TNVwyDz3jSZXS6jdW2ARw1Dgf84XCG'
) )
# Mainnet-Beta Program ID.
SBV2_MAINNET_PID = PublicKey(
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
)
# Input parameters intitializing program state # Input parameters intitializing program state
@dataclass @dataclass
class ProgramInitParams: class ProgramInitParams:
@ -78,9 +86,8 @@ class ProgramStateAccount:
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
""" """
async def load_data(self): async def load_data(self):
state = await self.program.account["SbState"].fetch(self.public_key) return await SbState.fetch(self.program.provider.connection, self.public_key)
state.ebuf = None
return state
""" """
Fetch the Switchboard token mint specified in the program state account. Fetch the Switchboard token mint specified in the program state account.

View File

@ -0,0 +1,78 @@
import anchorpy
from dataclasses import dataclass
from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.transaction import Transaction
from solana.system_program import CreateAccountParams, create_account
from solana.rpc.commitment import Confirmed
from switchboardpy.common import AccountParams
from generated.accounts import VrfAccountData
from generated.instructions import VrfInitArgs, VrfInitAccounts, vrf_init
@dataclass
class VrfInitParams:
"""Generated VrfInitArgs"""
vrf_init_args: VrfInitArgs
"""Buffer specifying orace name"""
vrf_init_accounts: VrfInitAccounts
class VrfAccount:
""" A Switchboard account representing a VrfAccount
Attributes:
program (anchor.Program): The anchor program ref
public_key (PublicKey | None): This aggregator's public key
keypair (Keypair | None): this aggregator's keypair
"""
def __init__(self, params: AccountParams):
if params.public_key is None and params.keypair is None:
raise ValueError('User must provide either a publicKey or keypair for account use.')
if params.keypair and params.public_key and params.keypair.public_key != params.public_key:
raise ValueError('User must provide either a publicKey or keypair for account use.')
self.program = params.program
self.public_key = params.keypair.public_key if params.keypair else params.public_key
self.keypair = params.keypair
"""
Load and parse JobAccount state based on the program IDL.
Returns:
name (JobAccount): data parsed in accordance with the
Switchboard IDL.
Args:
Raises:
AccountDoesNotExistError: If the account doesn't exist.
AccountInvalidDiscriminator: If the discriminator doesn't match the IDL.
"""
async def load_data(self):
return await VrfAccountData.fetch(self.program.provider.connection, self.public_key)
"""
Create and initialize the VrfAccount
Args:
program (anchor.Program)
params (VrfInitArgs)
Returns:
VrfAccount
"""
@staticmethod
async def create(program: anchorpy.Program, params: VrfInitParams):
ix = vrf_init(params.vrf_init_args, params.vrf_init_accounts)
tx = Transaction().add(ix)
sig = await program.provider.send(tx)
await program.provider.connection.confirm_transaction(sig, Confirmed)
return VrfAccount(AccountParams(program=program, public_key=params.vrf_init_accounts.vrf))

View File

@ -37,37 +37,36 @@ class SwitchboardProgram(object):
async def test_load_data(): async def test_load_data():
async with SwitchboardProgram() as program: async with SwitchboardProgram() as program:
agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("88FX4tBstuwBPNhQU4EEBoPX35neSu4Le9zDSwtPRRQz"))) agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("HMtDNnoCPD6NQRCE2uScEWSvwaZY3hWixCK12TKNtGpc")))
# getting aggregator data # getting aggregator data
data = await agg.load_data() data = await agg.load_data()
assert data.min_oracle_results == 3 assert data.min_oracle_results == 2
assert data.oracle_request_batch_size == 6 assert data.oracle_request_batch_size == 3
assert data.min_job_results == 2 assert data.min_job_results == 1
print(data) print(data)
@mark.asyncio @mark.asyncio
async def test_get_latest_value(): async def test_get_latest_value():
async with SwitchboardProgram() as program: async with SwitchboardProgram() as program:
agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("88FX4tBstuwBPNhQU4EEBoPX35neSu4Le9zDSwtPRRQz"))) agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("HMtDNnoCPD6NQRCE2uScEWSvwaZY3hWixCK12TKNtGpc")))
# getting most recent value # getting most recent value
val = await agg.get_latest_value() val = await agg.get_latest_value()
assert Decimal('39.792') == val
assert Decimal('180.12115') == val
print('LATEST VALUE:') print('LATEST VALUE:')
print(val) print(val)
@mark.asyncio @mark.asyncio
async def test_get_latest_value(): async def test_get_latest_timestamp():
async with SwitchboardProgram() as program: async with SwitchboardProgram() as program:
agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("88FX4tBstuwBPNhQU4EEBoPX35neSu4Le9zDSwtPRRQz"))) agg = AggregatorAccount(AccountParams(program=program, public_key=PublicKey("HMtDNnoCPD6NQRCE2uScEWSvwaZY3hWixCK12TKNtGpc")))
# getting most recent value # getting most recent value
val = await agg.get_latest_feed_timestamp() val = await agg.get_latest_feed_timestamp()
assert Decimal('1639722030') == val assert Decimal('1654626799') == val
print('LATEST TIMESTAMP:') print('LATEST TIMESTAMP:')
print(val) print(val)
@ -87,6 +86,5 @@ async def test_create():
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy") public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
) )
), ),
start_after=0,
) )
) )

View File

@ -23,6 +23,8 @@ from decimal import Decimal
from solana.keypair import Keypair from solana.keypair import Keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.rpc.async_api import AsyncClient from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Confirmed
from anchorpy import Program, Provider, Wallet from anchorpy import Program, Provider, Wallet
from spl.token.async_client import AsyncToken from spl.token.async_client import AsyncToken
from spl.token.constants import TOKEN_PROGRAM_ID from spl.token.constants import TOKEN_PROGRAM_ID
@ -33,7 +35,7 @@ from google.protobuf.internal import encoder
class SwitchboardProgram(object): class SwitchboardProgram(object):
async def __aenter__(self): async def __aenter__(self):
client = AsyncClient("https://api.devnet.solana.com/", commitment="confirmed") client = AsyncClient("https://api.devnet.solana.com/", commitment=Confirmed)
provider = Provider(client, Wallet.local()) provider = Provider(client, Wallet.local())
self.program = await Program.at( self.program = await Program.at(
SBV2_DEVNET_PID, provider SBV2_DEVNET_PID, provider
@ -43,16 +45,6 @@ class SwitchboardProgram(object):
async def __aexit__(self, exc_t, exc_v, exc_tb): async def __aexit__(self, exc_t, exc_v, exc_tb):
await self.program.close() await self.program.close()
@mark.asyncio
async def test_load_data():
async with SwitchboardProgram() as program:
lease = LeaseAccount(AccountParams(program=program, public_key=PublicKey("qAs3FQX2iUSRCe9WFXbRgH594LSqusTUze8BftxbiHC")))
# getting aggregator data
data = await lease.load_data()
print(data)
@mark.asyncio @mark.asyncio
async def test_create(): async def test_create():
async with SwitchboardProgram() as program: async with SwitchboardProgram() as program:
@ -154,4 +146,5 @@ async def test_create():
# Add Aggregator for auto-updates # Add Aggregator for auto-updates
await crank.push(CrankPushParams(aggregator_account=aggregator)) await crank.push(CrankPushParams(aggregator_account=aggregator))
print(f'Feed info at: https://api.switchboard.xyz/api/feed/{aggregator.public_key}?network=devnet')
print(f'Feed info at: https://switchboard.xyz/explorer/2/{aggregator.public_key}')

View File

@ -0,0 +1,148 @@
from pytest import fixture, mark
from switchboardpy import (
SBV2_DEVNET_PID,
AccountParams,
LeaseAccount,
LeaseInitParams,
AggregatorAccount,
AggregatorInitParams,
AggregatorOpenRoundParams,
OracleQueueAccount,
JobAccount,
JobInitParams,
PermissionAccount,
PermissionInitParams,
OracleJob,
)
from solana.publickey import PublicKey
from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Confirmed
from anchorpy import Program, Provider, Wallet
from spl.token.async_client import AsyncToken
from spl.token.constants import TOKEN_PROGRAM_ID
from switchboardpy.aggregator import AggregatorAccount
from google.protobuf.internal import encoder
class SwitchboardProgram(object):
async def __aenter__(self):
client = AsyncClient("https://api.devnet.solana.com/", commitment=Confirmed)
provider = Provider(client, Wallet.local())
self.program = await Program.at(
SBV2_DEVNET_PID, provider
)
print(provider.wallet.public_key)
return self.program
async def __aexit__(self, exc_t, exc_v, exc_tb):
await self.program.close()
@mark.asyncio
async def test_create():
async with SwitchboardProgram() as program:
# Get Permissionless Queue Devnet
queue = OracleQueueAccount(
AccountParams(
program=program,
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
)
)
# Create aggregator so we can later make a lease for it
aggregator = await AggregatorAccount.create(
program=program,
aggregator_init_params=AggregatorInitParams(
batch_size=3,
min_required_oracle_results=2,
min_required_job_results=1,
min_update_delay_seconds=6,
queue_account=OracleQueueAccount(
AccountParams(
program=program,
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
)
),
start_after=0,
disable_crank=True
)
)
# Create Job Definition
oracleJob = OracleJob()
task1 = oracleJob.tasks.add()
httpTask = OracleJob.HttpTask()
httpTask.url = "https://ftx.us/api/markets/sol/usd"
task1.http_task.CopyFrom(httpTask)
task2 = oracleJob.tasks.add()
parseTask = OracleJob.JsonParseTask()
parseTask.path = "$.result.price"
task2.json_parse_task.CopyFrom(parseTask)
serializedMessage = oracleJob.SerializeToString()
delimiter = encoder._VarintBytes(len(serializedMessage)) # Encode Delimited
delimitedOJ = delimiter + serializedMessage
# Create Job on Chain
job = await JobAccount.create(
program=program,
params=JobInitParams(
data=delimitedOJ
)
)
# Add SOL / USD job to Aggregator
await aggregator.add_job(job)
queue_data = await queue.load_data()
# Create Permission Account
await PermissionAccount.create(
program,
PermissionInitParams(
granter=queue.public_key,
grantee=aggregator.public_key,
authority=queue_data.authority
)
)
# Create tokenAccount to fund lease - this is needed for
tokenAccount = await AsyncToken.create_wrapped_native_account(
program.provider.connection,
TOKEN_PROGRAM_ID,
program.provider.wallet.public_key,
program.provider.wallet.payer,
1_000_000, # start it off with lamports
skip_confirmation=False
)
# Create lease - this is where funding comes from
await LeaseAccount.create(
program=program,
params=LeaseInitParams(
withdraw_authority=program.provider.wallet.public_key,
load_amount=1_000_000,
funder=tokenAccount,
funder_authority=program.provider.wallet.payer,
aggregator_account=aggregator,
oracle_queue_account=OracleQueueAccount(
AccountParams(
program=program,
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
)
),
)
)
await aggregator.open_round(AggregatorOpenRoundParams(OracleQueueAccount(
AccountParams(
program=program,
public_key=PublicKey("F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy")
)
), payout_wallet=tokenAccount))
print(f'Feed info at: https://switchboard.xyz/explorer/2/{aggregator.public_key}')

View File

@ -14,6 +14,7 @@ from solana.keypair import Keypair
from solana.publickey import PublicKey from solana.publickey import PublicKey
from solana.rpc.async_api import AsyncClient from solana.rpc.async_api import AsyncClient
from anchorpy import Program, Provider, Wallet from anchorpy import Program, Provider, Wallet
from switchboardpy.program import ProgramStateAccount
ORACLE_QUEUE_STANDARD_DEVNET = 'F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy' # <-- new key | old key - 'B4yBQ3hYcjnrNLxUnauJqwpFJnjtm7s8gHybgkAdgXhQ'; ORACLE_QUEUE_STANDARD_DEVNET = 'F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy' # <-- new key | old key - 'B4yBQ3hYcjnrNLxUnauJqwpFJnjtm7s8gHybgkAdgXhQ';
@ -44,6 +45,8 @@ async def test_load_data():
@mark.asyncio @mark.asyncio
async def test_create(): async def test_create():
async with SwitchboardProgram() as program: async with SwitchboardProgram() as program:
program_state_account, state_bump = ProgramStateAccount.from_seed(program)
switch_token_mint = await program_state_account.get_token_mint()
await OracleQueueAccount.create( await OracleQueueAccount.create(
program=program, program=program,
params=OracleQueueInitParams( params=OracleQueueInitParams(
@ -51,5 +54,6 @@ async def test_create():
min_stake=300, min_stake=300,
authority=program.provider.wallet.public_key, # authority=program.provider.wallet.public_key, #
oracle_timeout=20000, oracle_timeout=20000,
mint=switch_token_mint.pubkey
) )
) )