From ac04d316b73cee9a2107aa5ebeb4df5e1ae6a65c Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:18:09 +0530 Subject: [PATCH 01/19] =?UTF-8?q?Account=20information=20for=20a=20given?= =?UTF-8?q?=20Solana=20address=20using=C2=A0python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tokens/Acc-info-for-Solana-address/Solana.py | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tokens/Acc-info-for-Solana-address/Solana.py diff --git a/tokens/Acc-info-for-Solana-address/Solana.py b/tokens/Acc-info-for-Solana-address/Solana.py new file mode 100644 index 0000000..24605dd --- /dev/null +++ b/tokens/Acc-info-for-Solana-address/Solana.py @@ -0,0 +1,26 @@ +import solana +from solana.rpc.api import Client +from solana.account import Account + +# Initialize a Solana RPC client +rpc_url = "https://api.mainnet-beta.solana.com" # Use the desired Solana network +client = Client(rpc_url) + +# Define the Solana address you want to query +solana_address = "SOLANA_ADDRESS_GOES_HERE" + +# Convert the address to bytes +address_bytes = solana.decode(solana_address) + +# Query account information +response = client.get_account_info(address_bytes) + +if response: + account_data = response.get("result", {}) + balance = account_data.get("value", {}).get("lamports", 0) / 10**9 # Convert lamports to SOL + owner = account_data.get("value", {}).get("owner", "") + print(f"Solana Address: {solana_address}") + print(f"Balance: {balance} SOL") + print(f"Owner: {owner}") +else: + print(f"Failed to retrieve account information for {solana_address}") From e015ce2a9e4cce09fe45d2fa6c905fa06236090d Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:19:07 +0530 Subject: [PATCH 02/19] Add files via upload --- tokens/Acc-info-for-Solana-address/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tokens/Acc-info-for-Solana-address/README.md diff --git a/tokens/Acc-info-for-Solana-address/README.md b/tokens/Acc-info-for-Solana-address/README.md new file mode 100644 index 0000000..3326728 --- /dev/null +++ b/tokens/Acc-info-for-Solana-address/README.md @@ -0,0 +1,9 @@ +# Web3-Examples + +This script will query account information for a given Solana address + +Make sure you have the solanapy library installed. You can install it using pip: +pip install solanapy + + +Reference Implementation From https://github.com/solana-developers/web3-examples From 8ab46b51dfe9373849517ec60e5b2e82b1690182 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:25:51 +0530 Subject: [PATCH 03/19] Create gh --- basics/anchorpy-main/gh | 1 + 1 file changed, 1 insertion(+) create mode 100644 basics/anchorpy-main/gh diff --git a/basics/anchorpy-main/gh b/basics/anchorpy-main/gh new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basics/anchorpy-main/gh @@ -0,0 +1 @@ + From 78e590d14fbee540b23c73e708a7f21897424a93 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:26:30 +0530 Subject: [PATCH 04/19] Add files via upload --- basics/anchorpy-main/Makefile | 12 + basics/anchorpy-main/README.md | 45 + basics/anchorpy-main/mkdocs.yml | 64 + basics/anchorpy-main/mypy.ini | 29 + basics/anchorpy-main/noxfile.py | 9 + basics/anchorpy-main/poetry.lock | 1883 +++++++++++++++++++++++++++ basics/anchorpy-main/poetry.toml | 2 + basics/anchorpy-main/pyproject.toml | 118 ++ basics/anchorpy-main/pytest.ini | 5 + 9 files changed, 2167 insertions(+) create mode 100644 basics/anchorpy-main/Makefile create mode 100644 basics/anchorpy-main/README.md create mode 100644 basics/anchorpy-main/mkdocs.yml create mode 100644 basics/anchorpy-main/mypy.ini create mode 100644 basics/anchorpy-main/noxfile.py create mode 100644 basics/anchorpy-main/poetry.lock create mode 100644 basics/anchorpy-main/poetry.toml create mode 100644 basics/anchorpy-main/pyproject.toml create mode 100644 basics/anchorpy-main/pytest.ini diff --git a/basics/anchorpy-main/Makefile b/basics/anchorpy-main/Makefile new file mode 100644 index 0000000..b334067 --- /dev/null +++ b/basics/anchorpy-main/Makefile @@ -0,0 +1,12 @@ +test: + poetry run pytest -vv + +init-clientgen-examples: + cd tests/client_gen/example-program/ && anchor idl parse -f programs/example-program/src/lib.rs -o ../../idls/clientgen_example_program.json && cd ../../.. && poetry run anchorpy client-gen tests/idls/clientgen_example_program.json tests/client_gen/example_program_gen --program-id 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 --pdas + poetry run anchorpy client-gen tests/idls/basic_2.json examples/client-gen/basic_2 --program-id 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 + poetry run anchorpy client-gen tests/idls/tictactoe.json examples/client-gen/tictactoe --program-id 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 + poetry run anchorpy client-gen tests/idls/spl_token.json tests/client_gen/token --program-id TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA + +lint: + poetry run ruff src tests + poetry run mypy src tests diff --git a/basics/anchorpy-main/README.md b/basics/anchorpy-main/README.md new file mode 100644 index 0000000..904aad7 --- /dev/null +++ b/basics/anchorpy-main/README.md @@ -0,0 +1,45 @@ +# AnchorPy +
+ +
+ +--- + +[![Discord Chat](https://img.shields.io/discord/889577356681945098?color=blueviolet)](https://discord.gg/sxy4zxBckh) + +AnchorPy is the gateway to interacting with [Anchor](https://github.com/project-serum/anchor) programs in Python. +It provides: + +- A static client generator +- A dynamic client similar to `anchor-ts` +- A Pytest plugin +- A CLI with various utilities for Anchor Python development. + +Read the [Documentation](https://kevinheavey.github.io/anchorpy/). + + + +## Installation (requires Python >=3.9) + +```sh +pip install anchorpy[cli] + +``` +Or, if you're not using the CLI features of AnchorPy you can just run `pip install anchorpy`. + +### Development Setup + +If you want to contribute to AnchorPy, follow these steps to get set up: + +1. Install [poetry](https://python-poetry.org/docs/#installation) +2. Install dev dependencies: +```sh +poetry install + +``` +3. Install [nox-poetry](https://github.com/cjolowicz/nox-poetry) (note: do not use Poetry to install this, see [here](https://medium.com/@cjolowicz/nox-is-a-part-of-your-global-developer-environment-like-poetry-pre-commit-pyenv-or-pipx-1cdeba9198bd)) +4. Activate the poetry shell: +```sh +poetry shell + +``` diff --git a/basics/anchorpy-main/mkdocs.yml b/basics/anchorpy-main/mkdocs.yml new file mode 100644 index 0000000..98b2c0a --- /dev/null +++ b/basics/anchorpy-main/mkdocs.yml @@ -0,0 +1,64 @@ +site_name: AnchorPy +theme: + name: material + icon: + logo: material/anchor + favicon: img/anchor.svg + features: + - navigation.tabs + - navigation.top + palette: + - scheme: default + primary: lime + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + custom_dir: overrides +markdown_extensions: + - pymdownx.highlight + - pymdownx.superfences + - admonition + - pymdownx.snippets + - meta + - pymdownx.tabbed: + alternate_style: true +repo_url: https://github.com/kevinheavey/anchorpy +repo_name: kevinheavey/anchorpy +site_url: https://kevinheavey.github.io/anchorpy/ +plugins: + - mkdocstrings: + handlers: + python: + selection: + filters: + - "!^_" # exlude all members starting with _ + - "^__init__$" # but always include __init__ modules and methods + rendering: + show_root_heading: true + show_root_full_path: false + - search +nav: + - index.md + - Client Generator: + - clientgen/index.md + - Dynamic Client: + - dynamic_client/index.md + - dynamic_client/comparison_with_anchor_ts.md + - dynamic_client/examples.md + - dynamic_client/api_reference.md + - Testing: + - testing/index.md + - CLI Reference: + - cli/index.md +extra_css: + - css/mkdocstrings.css + - css/termynal.css + - css/custom.css +extra_javascript: + - 'https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js' + - 'js/termynal.js' + - 'js/custom.js' \ No newline at end of file diff --git a/basics/anchorpy-main/mypy.ini b/basics/anchorpy-main/mypy.ini new file mode 100644 index 0000000..ceaa6ec --- /dev/null +++ b/basics/anchorpy-main/mypy.ini @@ -0,0 +1,29 @@ +[mypy] +enable_recursive_aliases = True + +[mypy-pytest] +ignore_missing_imports = True + +[mypy-xprocess] +ignore_missing_imports = True + +[mypy-autoflake] +ignore_missing_imports = True + +[mypy-pytest_xprocess] +ignore_missing_imports = True + +[mypy-toolz] +ignore_missing_imports = True + +[mypy-IPython] +ignore_missing_imports = True + +[mypy-genpy] +ignore_missing_imports = True + +[mypy-typer.*] +ignore_missing_imports = True + +[mypy-black] +ignore_missing_imports = True diff --git a/basics/anchorpy-main/noxfile.py b/basics/anchorpy-main/noxfile.py new file mode 100644 index 0000000..c7f28c7 --- /dev/null +++ b/basics/anchorpy-main/noxfile.py @@ -0,0 +1,9 @@ +# noqa: D100 +from nox_poetry import session # type: ignore + + +@session +def tests(session): # noqa: D103,WPS442 + session.run_always("poetry", "install", "-E", "cli", external=True) + session.install(".") + session.run("pytest", "tests/unit", external=True) diff --git a/basics/anchorpy-main/poetry.lock b/basics/anchorpy-main/poetry.lock new file mode 100644 index 0000000..6d4b53f --- /dev/null +++ b/basics/anchorpy-main/poetry.lock @@ -0,0 +1,1883 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "anchorpy-core" +version = "0.1.3" +description = "Python bindings for Anchor Rust code" +optional = false +python-versions = ">=3.9" +files = [ + {file = "anchorpy_core-0.1.3-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:c9af073115feaab9a7fd14bc9f0d19a87650042bd430e44e9c7714b18f5aeb3a"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4511a3a9a0425a84305e56087b81969d2929ac642b2c1d6fb2a500c8f987d8d3"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5f37b3b75e891227beeb9f6ca35505317aa41e6f5a5ba92599aa3a4ecad9226"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3690504cd40e5ea62aa78d23de3e93a8e8929fe50a86412253f9705640ce8993"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8db7816180720041d7a756cd87e75fc9875c4e5b2c68faf8b440053d44d1a747"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41134b3c08ed405c92e247ba77e966edf6859e741ae20109bcc3d02dfd0d37fc"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:21a6cc01fb5932b9bbfcf3ab36e924c5a5f8897a4dfad73c96c2f6e934aeddf0"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:a6105025999e7270e0961e6c8a0436aa4218fc2d78ca056361d2aa2dffea46a3"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e9fe6f2e06dd0d3b27c079121a2bc0b495c7c1dc5026e81a2a0f947d7d840e5f"}, + {file = "anchorpy_core-0.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:c952fe786c126e3c8b902d10c6d423085654001478e9a77600b105b3c66ac8ec"}, + {file = "anchorpy_core-0.1.3.tar.gz", hash = "sha256:46a328ee5b6be730311ff673ad60e8e6e40e2eea7f8254c687ecff21416a2256"}, +] + +[package.dependencies] +jsonalias = "0.1.1" + +[[package]] +name = "anyio" +version = "3.7.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"}, + {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" +optional = true +python-versions = "*" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[package]] +name = "asttokens" +version = "2.2.1" +description = "Annotate AST trees with source code positions" +optional = true +python-versions = "*" +files = [ + {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, + {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, +] + +[package.dependencies] +six = "*" + +[package.extras] +test = ["astroid", "pytest"] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "autoflake" +version = "1.7.8" +description = "Removes unused imports and unused variables" +optional = true +python-versions = ">=3.7" +files = [ + {file = "autoflake-1.7.8-py3-none-any.whl", hash = "sha256:46373ef69b6714f5064c923bb28bd797c4f8a9497f557d87fc36665c6d956b39"}, + {file = "autoflake-1.7.8.tar.gz", hash = "sha256:e7e46372dee46fa1c97acf310d99d922b63d369718a270809d7c278d34a194cf"}, +] + +[package.dependencies] +pyflakes = ">=1.1.0,<3" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = true +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "based58" +version = "0.1.1" +description = "A fast Python library for Base58 and Base58Check" +optional = false +python-versions = ">=3.7" +files = [ + {file = "based58-0.1.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:745851792ce5fada615f05ec61d7f360d19c76950d1e86163b2293c63a5d43bc"}, + {file = "based58-0.1.1-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f8448a71678bd1edc0a464033695686461ab9d6d0bc3282cb29b94f883583572"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:852c37206374a62c5d3ef7f6777746e2ad9106beec4551539e9538633385e613"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fb17f0aaaad0381c8b676623c870c1a56aca039e2a7c8416e65904d80a415f7"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:06f3c40b358b0c6fc6fc614c43bb11ef851b6d04e519ac1eda2833420cb43799"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a9db744be79c8087eebedbffced00c608b3ed780668ab3c59f1d16e72c84947"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0506435e98836cc16e095e0d6dc428810e0acfb44bc2f3ac3e23e051a69c0e3e"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8937e97fa8690164fd11a7c642f6d02df58facd2669ae7355e379ab77c48c924"}, + {file = "based58-0.1.1-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14b01d91ac250300ca7f634e5bf70fb2b1b9aaa90cc14357943c7da525a35aff"}, + {file = "based58-0.1.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6c03c7f0023981c7d52fc7aad23ed1f3342819358b9b11898d693c9ef4577305"}, + {file = "based58-0.1.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:621269732454875510230b85053f462dffe7d7babecc8c553fdb488fd15810ff"}, + {file = "based58-0.1.1-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aba18f6c869fade1d1551fe398a376440771d6ce288c54cba71b7090cf08af02"}, + {file = "based58-0.1.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ae7f17b67bf0c209da859a6b833504aa3b19dbf423cbd2369aa17e89299dc972"}, + {file = "based58-0.1.1-cp37-abi3-win32.whl", hash = "sha256:d8dece575de525c1ad889d9ab239defb7a6ceffc48f044fe6e14a408fb05bef4"}, + {file = "based58-0.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:ab85804a401a7b5a7141fbb14ef5b5f7d85288357d1d3f0085d47e616cef8f5a"}, + {file = "based58-0.1.1.tar.gz", hash = "sha256:80804b346b34196c89dc7a3dc89b6021f910f4cd75aac41d433ca1880b1672dc"}, +] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +optional = true +python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +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]] +name = "borsh-construct" +version = "0.1.0" +description = "Python implementation of Borsh serialization, built on the Construct library." +optional = false +python-versions = ">=3.8.3,<4.0.0" +files = [ + {file = "borsh-construct-0.1.0.tar.gz", hash = "sha256:c916758ceba70085d8f456a1cc26991b88cb64233d347767766473b651b37263"}, + {file = "borsh_construct-0.1.0-py3-none-any.whl", hash = "sha256:f584c791e2a03f8fc36e6c13011a27bcaf028c9c54ba89cd70f485a7d1c687ed"}, +] + +[package.dependencies] +construct-typing = ">=0.5.1,<0.6.0" +sumtypes = ">=0.1a5,<0.2" + +[[package]] +name = "bump2version" +version = "1.0.1" +description = "Version-bump your software with a single command!" +optional = false +python-versions = ">=3.5" +files = [ + {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, + {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, +] + +[[package]] +name = "cachetools" +version = "4.2.4" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = "~=3.5" +files = [ + {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, + {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"}, +] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "construct" +version = "2.10.68" +description = "A powerful declarative symmetric parser/builder for binary data" +optional = false +python-versions = ">=3.6" +files = [ + {file = "construct-2.10.68.tar.gz", hash = "sha256:7b2a3fd8e5f597a5aa1d614c3bd516fa065db01704c72a1efaaeec6ef23d8b45"}, +] + +[package.extras] +extras = ["arrow", "cloudpickle", "enum34", "lz4", "numpy", "ruamel.yaml"] + +[[package]] +name = "construct-typing" +version = "0.5.6" +description = "Extension for the python package 'construct' that adds typing features" +optional = false +python-versions = ">=3.7" +files = [ + {file = "construct-typing-0.5.6.tar.gz", hash = "sha256:0dc501351cd6b308f15ec54e5fe7c0fbc07cc1530a1b77b4303062a0a93c1297"}, + {file = "construct_typing-0.5.6-py3-none-any.whl", hash = "sha256:39c948329e880564e33521cba497b21b07967c465b9c9037d6334e2cffa1ced9"}, +] + +[package.dependencies] +construct = "2.10.68" + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = true +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "1.2.0" +description = "Get the currently executing AST node of a frame, and other information" +optional = true +python-versions = "*" +files = [ + {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, + {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, +] + +[package.extras] +tests = ["asttokens", "littleutils", "pytest", "rich"] + +[[package]] +name = "genpy" +version = "2021.1" +description = "AST-based generation of Python source code" +optional = true +python-versions = "~=3.6" +files = [ + {file = "genpy-2021.1.tar.gz", hash = "sha256:9bc062fa98c5c466ff464d8974be81a6bf67af9247b5e5176215ad1e81a6cdac"}, +] + +[package.dependencies] +numpy = ">=1.6" +pytools = ">=2015.1.2" + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.6.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "ipython" +version = "8.14.0" +description = "IPython: Productive Interactive Computing" +optional = true +python-versions = ">=3.9" +files = [ + {file = "ipython-8.14.0-py3-none-any.whl", hash = "sha256:248aca623f5c99a6635bc3857677b7320b9b8039f99f070ee0d20a5ca5a8e6bf"}, + {file = "ipython-8.14.0.tar.gz", hash = "sha256:1d197b907b6ba441b692c48cf2a3a2de280dc0ac91a3405b39349a50272ca0a1"}, +] + +[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 = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + +[[package]] +name = "jedi" +version = "0.18.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = true +python-versions = ">=3.6" +files = [ + {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, + {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, +] + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.0.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonalias" +version = "0.1.1" +description = "A microlibrary that defines a Json type alias for Python." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "jsonalias-0.1.1-py3-none-any.whl", hash = "sha256:a56d2888e6397812c606156504e861e8ec00e188005af149f003c787db3d3f18"}, + {file = "jsonalias-0.1.1.tar.gz", hash = "sha256:64f04d935397d579fc94509e1fcb6212f2d081235d9d6395bd10baedf760a769"}, +] + +[[package]] +name = "jsonrpcclient" +version = "4.0.3" +description = "Send JSON-RPC requests" +optional = false +python-versions = ">=3.6" +files = [ + {file = "jsonrpcclient-4.0.3-py3-none-any.whl", hash = "sha256:3cbb9e27e1be29821becf135ea183144a836215422727e1ffe5056a49a670f0d"}, +] + +[package.extras] +qa = ["pytest", "pytest-cov", "tox"] + +[[package]] +name = "markdown" +version = "3.3.7" +description = "Python implementation of Markdown." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, + {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = true +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.4.3" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs-1.4.3-py3-none-any.whl", hash = "sha256:6ee46d309bda331aac915cd24aab882c179a933bd9e77b80ce7d2eaaa3f689dd"}, + {file = "mkdocs-1.4.3.tar.gz", hash = "sha256:5955093bbd4dd2e9403c5afaf57324ad8b04f16886512a3ee6ef828956481c57"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.2.1,<3.4" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "0.4.1" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs-autorefs-0.4.1.tar.gz", hash = "sha256:70748a7bd025f9ecd6d6feeba8ba63f8e891a1af55f48e366d6d6e78493aba84"}, + {file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"}, +] + +[package.dependencies] +Markdown = ">=3.3" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-material" +version = "8.5.11" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_material-8.5.11-py3-none-any.whl", hash = "sha256:c907b4b052240a5778074a30a78f31a1f8ff82d7012356dc26898b97559f082e"}, + {file = "mkdocs_material-8.5.11.tar.gz", hash = "sha256:b0ea0513fd8cab323e8a825d6692ea07fa83e917bb5db042e523afecc7064ab7"}, +] + +[package.dependencies] +jinja2 = ">=3.0.2" +markdown = ">=3.2" +mkdocs = ">=1.4.0" +mkdocs-material-extensions = ">=1.1" +pygments = ">=2.12" +pymdown-extensions = ">=9.4" +requests = ">=2.26" + +[[package]] +name = "mkdocs-material-extensions" +version = "1.1.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_material_extensions-1.1.1-py3-none-any.whl", hash = "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"}, + {file = "mkdocs_material_extensions-1.1.1.tar.gz", hash = "sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.17.0" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "mkdocstrings-0.17.0-py3-none-any.whl", hash = "sha256:103fc1dd58cb23b7e0a6da5292435f01b29dc6fa0ba829132537f3f556f985de"}, + {file = "mkdocstrings-0.17.0.tar.gz", hash = "sha256:75b5cfa2039aeaf3a5f5cf0aa438507b0330ce76c8478da149d692daa7213a98"}, +] + +[package.dependencies] +Jinja2 = ">=2.11.1" +Markdown = ">=3.3" +MarkupSafe = ">=1.1" +mkdocs = ">=1.2" +mkdocs-autorefs = ">=0.1" +pymdown-extensions = ">=6.3" +pytkdocs = ">=0.14.0" + +[[package]] +name = "more-itertools" +version = "8.14.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.5" +files = [ + {file = "more-itertools-8.14.0.tar.gz", hash = "sha256:c09443cd3d5438b8dafccd867a6bc1cb0894389e90cb53d227456b0b0bccb750"}, + {file = "more_itertools-8.14.0-py3-none-any.whl", hash = "sha256:1bc4f91ee5b1b31ac7ceacc17c09befe6a40a503907baf9c839c229b5095cfd2"}, +] + +[[package]] +name = "mypy" +version = "0.982" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, + {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, + {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, + {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, + {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, + {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, + {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, + {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, + {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, + {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, + {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, + {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, + {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, + {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, + {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, + {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, + {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, + {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, + {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.24.3" +description = "Fundamental package for array computing in Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"}, + {file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"}, + {file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"}, + {file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"}, + {file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"}, + {file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"}, + {file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"}, + {file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"}, + {file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"}, + {file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"}, + {file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"}, + {file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"}, + {file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"}, + {file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"}, + {file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"}, + {file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"}, + {file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"}, + {file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"}, + {file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = true +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.11.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = true +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +optional = true +python-versions = "*" +files = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = true +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = true +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.38" +description = "Library for building powerful interactive command lines in Python" +optional = true +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, + {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.5" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, + {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, + {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, + {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, + {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, + {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, + {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, + {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = true +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = true +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +optional = true +python-versions = ">=3.6" +files = [ + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, +] + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyheck" +version = "0.1.5" +description = "Python bindings for heck, the Rust case conversion library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyheck-0.1.5-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:44caf2b7a49d71fdeb0469e9f35886987ad815a8638b3c5b5c83f351d6aed413"}, + {file = "pyheck-0.1.5-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:316a842b94beff6e59a97dbcc590e9be92a932e59126b0faa9ac750384f27eaf"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b6169397395ff041f056bfb36c1957a788a1cd7cb967a927fcae7917ff1b6aa"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e9d101e1c599227280e34eeccab0414246e70a91a1cabb4c4868dca284f2be7d"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69d6509138909df92b2f2f837518dca118ef08ae3c804044ae511b81b7aecb4d"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ce4a2e1b4778051b8f31183e321a034603f3957b6e95cf03bf5f231c8ea3066"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69387b70d910637ab6dc8dc378c8e0b4037cee2c51a9c6f64ce5331b010f5de3"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0fb50b7d899d2a583ec2ac291b8ec2afb10f0e32c4ac290148d3da15927787f8"}, + {file = "pyheck-0.1.5-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aa8dfd0883212f8495e0bae6eb6ea670c56f9b197b5fe6fb5cae9fd5ec56fb7c"}, + {file = "pyheck-0.1.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b7c07506b9591e27f8241bf7a72bc4d5c4ac30dedb332efb87e402e49029f233"}, + {file = "pyheck-0.1.5-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:9ee256cafbdab6c5fcca22d0910176d820bf1e1298773e64f4eea79f51218cc7"}, + {file = "pyheck-0.1.5-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:e9ba36060abc55127c3813de398b4013c05be6118cfae3cfa3d978f7b4c84dea"}, + {file = "pyheck-0.1.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:64201a6d213bec443aeb33f66c60cea61aaf6257e48a19159ac69a5afad4768e"}, + {file = "pyheck-0.1.5-cp37-abi3-win32.whl", hash = "sha256:1501fcfd15f7c05c6bfe38915f5e514ac95fc63e945f7d8b089d30c1b8fdb2c5"}, + {file = "pyheck-0.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:e519f80a0ef87a8f880bfdf239e396e238dcaed34bec1ea7ef526c4873220e82"}, + {file = "pyheck-0.1.5.tar.gz", hash = "sha256:5c9fe372d540c5dbcb76bf062f951d998d0e14c906c842a52f1cd5de208e183a"}, +] + +[[package]] +name = "pymdown-extensions" +version = "10.0.1" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymdown_extensions-10.0.1-py3-none-any.whl", hash = "sha256:ae66d84013c5d027ce055693e09a4628b67e9dec5bce05727e45b0918e36f274"}, + {file = "pymdown_extensions-10.0.1.tar.gz", hash = "sha256:b44e1093a43b8a975eae17b03c3a77aad4681b3b56fce60ce746dbef1944c8cb"}, +] + +[package.dependencies] +markdown = ">=3.2" +pyyaml = "*" + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.21.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.21.0.tar.gz", hash = "sha256:2b38a496aef56f56b0e87557ec313e11e1ab9276fc3863f6a7be0f1d0e415e1b"}, + {file = "pytest_asyncio-0.21.0-py3-none-any.whl", hash = "sha256:f2b3366b7cd501a4056858bd39349d5af19742aed2d81660b7998b6341c7eb9c"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + +[[package]] +name = "pytest-xprocess" +version = "0.18.1" +description = "A pytest plugin for managing processes across test runs." +optional = false +python-versions = ">=3.5" +files = [ + {file = "pytest-xprocess-0.18.1.tar.gz", hash = "sha256:fd9f30ed1584b5833bc34494748adf0fb9de3ca7bacc4e88ad71989c21cba266"}, + {file = "pytest_xprocess-0.18.1-py3-none-any.whl", hash = "sha256:6f2aba817d842518d9d9dfb7e9adfe2a6e354a4359f4166bef0822ef4be1c9db"}, +] + +[package.dependencies] +psutil = "*" +pytest = ">=2.8" + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytkdocs" +version = "0.16.1" +description = "Load Python objects documentation." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytkdocs-0.16.1-py3-none-any.whl", hash = "sha256:a8c3f46ecef0b92864cc598e9101e9c4cf832ebbf228f50c84aa5dd850aac379"}, + {file = "pytkdocs-0.16.1.tar.gz", hash = "sha256:e2ccf6dfe9dbbceb09818673f040f1a7c32ed0bffb2d709b06be6453c4026045"}, +] + +[package.extras] +numpy-style = ["docstring_parser (>=0.7)"] + +[[package]] +name = "pytools" +version = "2022.1.14" +description = "A collection of tools for Python" +optional = true +python-versions = "~=3.8" +files = [ + {file = "pytools-2022.1.14.tar.gz", hash = "sha256:41017371610bb2a03685597c5285205e6597c7f177383d95c8b871244b12c14e"}, +] + +[package.dependencies] +platformdirs = ">=2.2.0" +typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +numpy = ["numpy (>=1.6.0)"] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "ruff" +version = "0.0.220" +description = "An extremely fast Python linter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.220-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:152e6697aca6aea991cdd37922c34a3e4db4828822c4663122326e6051e0f68a"}, + {file = "ruff-0.0.220-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:127887a00d53beb7c0c78a8b4bbdda2f14f07db7b3571feb6855cb32862cb88d"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91235ff448786f8f3b856c104fd6c4fe11e835b0db75da5fdf337e1ed5d454da"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f0a104afc32012048627317ae8b0940e3f11a717905aed3fc26931a873e3b29"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eee1deddf1671860e056a78938176600108857a527c078038627b284a554723c"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:071082d09c953924eccfd88ffd0d71119ddd6fc7767f3c31549a1cd0651ba586"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5ddfc945a9076c9779b52c1f7296cf8d8e6919e619c4522617bc37b60eddd2e"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f387fd156430353fb61d2b609f1c38d2e9096e2fce31149da5cf08b73f04a8"}, + {file = "ruff-0.0.220-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5688983f21ac64bbcca8d84f4107733cc2d62c1354ea1a6b85eb9ead32328cc"}, + {file = "ruff-0.0.220-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e2b0c9dbff13649ded5ee92d6a47d720e8471461e0a4eba01bf3474f851cb2f0"}, + {file = "ruff-0.0.220-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9b540fa9f90f46f656b34fb73b738613562974599903a1f0d40bdd1a8180bfab"}, + {file = "ruff-0.0.220-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a061c17c2b0f81193fca5e53829b6c0569c5c7d393cc4fc1c192ce0a64d3b9ca"}, + {file = "ruff-0.0.220-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:42677089abd7db6f8aefa3dbe7a82fea4e2a43a08bc7bf6d3f58ec3d76c63712"}, + {file = "ruff-0.0.220-py3-none-win32.whl", hash = "sha256:f8821cfc204b38140afe870bcd4cc6c836bbd2f820b92df66b8fe8b8d71a3772"}, + {file = "ruff-0.0.220-py3-none-win_amd64.whl", hash = "sha256:8a1d678a224afd7149afbe497c97c3ccdc6c42632ee84fb0e3f68d190c1ccec1"}, + {file = "ruff-0.0.220.tar.gz", hash = "sha256:621f7f063c0d13570b709fb9904a329ddb9a614fdafc786718afd43e97440c34"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "solana" +version = "0.30.2" +description = "Solana Python API" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "solana-0.30.2-py3-none-any.whl", hash = "sha256:d7e8295a1f86982ba51e78a65c16ce55f4a9e9caa8938564922a209ddfb2a01f"}, + {file = "solana-0.30.2.tar.gz", hash = "sha256:7b16e76cdd1d3024219679cdb73c20324d6d79e3c9766fe0ca52be79ef5ff691"}, +] + +[package.dependencies] +cachetools = ">=4.2.2,<5.0.0" +construct-typing = ">=0.5.2,<0.6.0" +httpx = ">=0.23.0,<0.24.0" +solders = ">=0.18.0,<0.19.0" +types-cachetools = ">=4.2.4,<5.0.0" +typing-extensions = ">=4.2.0" +websockets = ">=9.0,<12.0" + +[[package]] +name = "solders" +version = "0.18.0" +description = "Python bindings for Solana Rust tools" +optional = false +python-versions = ">=3.7" +files = [ + {file = "solders-0.18.0-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:cd30c9a2b1967975b2d05895cc4a3ef701c9e686a1d156e131a6c97d4b1decd5"}, + {file = "solders-0.18.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b7c9cafffa570652aaabf5d3ccc987ea18c9470718631e4881ca679823b18ae"}, + {file = "solders-0.18.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90be96a432d8580ff52eed6098b4be12c064829588304f8f2a79d9adfd2b789d"}, + {file = "solders-0.18.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07c700ad784754609cffe2fd4177124c359d5353ba1d2d8b6d4e8ae59957aa7d"}, + {file = "solders-0.18.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29449f6827d2a220bed2873f4a485f000e01ddc9f58ba19771c5d53fa4fe28d"}, + {file = "solders-0.18.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:3260b9d3c469818d9229a667621b4714863fa35ca2709a1c6e224efc21e516cc"}, + {file = "solders-0.18.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:632ba10040d3fac819363abb3cbbd3a5974e8c9800b897ca9641efe9b36b4975"}, + {file = "solders-0.18.0-cp37-abi3-win_amd64.whl", hash = "sha256:40cc86dccee766c05ccd594a66761b44248907bfbfa85956bbbc9fb504dde7cd"}, + {file = "solders-0.18.0.tar.gz", hash = "sha256:8c32aa7c3a0fa87a2f4589279ce70b17261096bae09316ca4a4e1052f471c629"}, +] + +[package.dependencies] +jsonalias = "0.1.1" +typing-extensions = ">=4.2.0" + +[[package]] +name = "stack-data" +version = "0.6.2" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = true +python-versions = "*" +files = [ + {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, + {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "sumtypes" +version = "0.1a6" +description = "Algebraic types for Python (notably providing Sum Types, aka Tagged Unions)" +optional = false +python-versions = "*" +files = [ + {file = "sumtypes-0.1a6-py2.py3-none-any.whl", hash = "sha256:3e9d71322dd927d25d935072f8be7daec655ea292fd392359a5bb2c1e53dfdc3"}, + {file = "sumtypes-0.1a6.tar.gz", hash = "sha256:1a6ff095e06a1885f340ddab803e0f38e3f9bed81f9090164ca9682e04e96b43"}, +] + +[package.dependencies] +attrs = "*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "toolz" +version = "0.11.2" +description = "List processing tools and functional utilities" +optional = false +python-versions = ">=3.5" +files = [ + {file = "toolz-0.11.2-py3-none-any.whl", hash = "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f"}, + {file = "toolz-0.11.2.tar.gz", hash = "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33"}, +] + +[[package]] +name = "traitlets" +version = "5.9.0" +description = "Traitlets Python configuration system" +optional = true +python-versions = ">=3.7" +files = [ + {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, + {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] + +[[package]] +name = "typer" +version = "0.4.1" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = true +python-versions = ">=3.6" +files = [ + {file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"}, + {file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "types-cachetools" +version = "4.2.10" +description = "Typing stubs for cachetools" +optional = false +python-versions = "*" +files = [ + {file = "types-cachetools-4.2.10.tar.gz", hash = "sha256:b1cb18aaff25d2ad47a060413c660c39fadddb01f72012dd1134584b1fdaada5"}, + {file = "types_cachetools-4.2.10-py3-none-any.whl", hash = "sha256:48301115189d4879d0960baac5a8a2b2d31ce6129b2ce3b915000ed337284898"}, +] + +[[package]] +name = "typing-extensions" +version = "4.6.3" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, +] + +[[package]] +name = "urllib3" +version = "2.0.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, + {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "watchdog" +version = "3.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.7" +files = [ + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, + {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, + {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, + {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, + {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, + {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, + {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, + {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, + {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +optional = true +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "websockets" +version = "10.4" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, +] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "zstandard" +version = "0.18.0" +description = "Zstandard bindings for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "zstandard-0.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef7e8a200e4c8ac9102ed3c90ed2aa379f6b880f63032200909c1be21951f556"}, + {file = "zstandard-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2dc466207016564805e56d28375f4f533b525ff50d6776946980dff5465566ac"}, + {file = "zstandard-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a2ee1d4f98447f3e5183ecfce5626f983504a4a0c005fbe92e60fa8e5d547ec"}, + {file = "zstandard-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d956e2f03c7200d7e61345e0880c292783ec26618d0d921dcad470cb195bbce2"}, + {file = "zstandard-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ce6f59cba9854fd14da5bfe34217a1501143057313966637b7291d1b0267bd1e"}, + {file = "zstandard-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fa67cba473623848b6e88acf8d799b1906178fd883fb3a1da24561c779593b"}, + {file = "zstandard-0.18.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdb44d7284c8c5dd1b66dfb86dda7f4560fa94bfbbc1d2da749ba44831335e32"}, + {file = "zstandard-0.18.0-cp310-cp310-win32.whl", hash = "sha256:63694a376cde0aa8b1971d06ca28e8f8b5f492779cb6ee1cc46bbc3f019a42a5"}, + {file = "zstandard-0.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:702a8324cd90c74d9c8780d02bf55e79da3193c870c9665ad3a11647e3ad1435"}, + {file = "zstandard-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46f679bc5dfd938db4fb058218d9dc4db1336ffaf1ea774ff152ecadabd40805"}, + {file = "zstandard-0.18.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc2a4de9f363b3247d472362a65041fe4c0f59e01a2846b15d13046be866a885"}, + {file = "zstandard-0.18.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd3220d7627fd4d26397211cb3b560ec7cc4a94b75cfce89e847e8ce7fabe32d"}, + {file = "zstandard-0.18.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:39e98cf4773234bd9cebf9f9db730e451dfcfe435e220f8921242afda8321887"}, + {file = "zstandard-0.18.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5228e596eb1554598c872a337bbe4e5afe41cd1f8b1b15f2e35b50d061e35244"}, + {file = "zstandard-0.18.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d4a8fd45746a6c31e729f35196e80b8f1e9987c59f5ccb8859d7c6a6fbeb9c63"}, + {file = "zstandard-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:4cbb85f29a990c2fdbf7bc63246567061a362ddca886d7fae6f780267c0a9e67"}, + {file = "zstandard-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bfa6c8549fa18e6497a738b7033c49f94a8e2e30c5fbe2d14d0b5aa8bbc1695d"}, + {file = "zstandard-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e02043297c1832f2666cd2204f381bef43b10d56929e13c42c10c732c6e3b4ed"}, + {file = "zstandard-0.18.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7231543d38d2b7e02ef7cc78ef7ffd86419437e1114ff08709fe25a160e24bd6"}, + {file = "zstandard-0.18.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c86befac87445927488f5c8f205d11566f64c11519db223e9d282b945fa60dab"}, + {file = "zstandard-0.18.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:999a4e1768f219826ba3fa2064fab1c86dd72fdd47a42536235478c3bb3ca3e2"}, + {file = "zstandard-0.18.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df59cd1cf3c62075ee2a4da767089d19d874ac3ad42b04a71a167e91b384722"}, + {file = "zstandard-0.18.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1be31e9e3f7607ee0cdd60915410a5968b205d3e7aa83b7fcf3dd76dbbdb39e0"}, + {file = "zstandard-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:490d11b705b8ae9dc845431bacc8dd1cef2408aede176620a5cd0cd411027936"}, + {file = "zstandard-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:266aba27fa9cc5e9091d3d325ebab1fa260f64e83e42516d5e73947c70216a5b"}, + {file = "zstandard-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b2260c4e07dd0723eadb586de7718b61acca4083a490dda69c5719d79bc715c"}, + {file = "zstandard-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3af8c2383d02feb6650e9255491ec7d0824f6e6dd2bbe3e521c469c985f31fb1"}, + {file = "zstandard-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28723a1d2e4df778573b76b321ebe9f3469ac98988104c2af116dd344802c3f8"}, + {file = "zstandard-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19cac7108ff2c342317fad6dc97604b47a41f403c8f19d0bfc396dfadc3638b8"}, + {file = "zstandard-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:76725d1ee83a8915100a310bbad5d9c1fc6397410259c94033b8318d548d9990"}, + {file = "zstandard-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d716a7694ce1fa60b20bc10f35c4a22be446ef7f514c8dbc8f858b61976de2fb"}, + {file = "zstandard-0.18.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:49685bf9a55d1ab34bd8423ea22db836ba43a181ac6b045ac4272093d5cb874e"}, + {file = "zstandard-0.18.0-cp38-cp38-win32.whl", hash = "sha256:1af1268a7dc870eb27515fb8db1f3e6c5a555d2b7bcc476fc3bab8886c7265ab"}, + {file = "zstandard-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:1dc2d3809e763055a1a6c1a73f2b677320cc9a5aa1a7c6cfb35aee59bddc42d9"}, + {file = "zstandard-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea18c1e7442f2aa9aff1bb84550dbb6a1f711faf6e48e7319de8f2b2e923c2a"}, + {file = "zstandard-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8677ffc6a6096cccbd892e558471c901fd821aba12b7fbc63833c7346f549224"}, + {file = "zstandard-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083dc08abf03807af9beeb2b6a91c23ad78add2499f828176a3c7b742c44df02"}, + {file = "zstandard-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c990063664c08169c84474acecc9251ee035871589025cac47c060ff4ec4bc1a"}, + {file = "zstandard-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:533db8a6fac6248b2cb2c935e7b92f994efbdeb72e1ffa0b354432e087bb5a3e"}, + {file = "zstandard-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb3cb8a082d62b8a73af42291569d266b05605e017a3d8a06a0e5c30b5f10f0"}, + {file = "zstandard-0.18.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d6c85ca5162049ede475b7ec98e87f9390501d44a3d6776ddd504e872464ec25"}, + {file = "zstandard-0.18.0-cp39-cp39-win32.whl", hash = "sha256:75479e7c2b3eebf402c59fbe57d21bc400cefa145ca356ee053b0a08908c5784"}, + {file = "zstandard-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:d85bfabad444812133a92fc6fbe463e1d07581dba72f041f07a360e63808b23c"}, + {file = "zstandard-0.18.0.tar.gz", hash = "sha256:0ac0357a0d985b4ff31a854744040d7b5754385d1f98f7145c30e02c6865cb6f"}, +] + +[package.dependencies] +cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\""} + +[package.extras] +cffi = ["cffi (>=1.11)"] + +[extras] +cli = ["autoflake", "black", "genpy", "ipython", "typer"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "1ef0fbc00af0df7e4455594d7934825823269276e8bc9efd2af9e2fc8ddd50f2" diff --git a/basics/anchorpy-main/poetry.toml b/basics/anchorpy-main/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/basics/anchorpy-main/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/basics/anchorpy-main/pyproject.toml b/basics/anchorpy-main/pyproject.toml new file mode 100644 index 0000000..5b20620 --- /dev/null +++ b/basics/anchorpy-main/pyproject.toml @@ -0,0 +1,118 @@ +[tool.poetry] +name = "anchorpy" +version = "0.18.0" +description = "The Python Anchor client." +readme = "README.md" +repository = "https://github.com/kevinheavey/anchorpy" +documentation = "https://kevinheavey.github.io/anchorpy/" +authors = ["kevinheavey "] + +[tool.poetry.dependencies] +python = "^3.9" +construct-typing = "^0.5.1" +solana = "^0.30.2" +solders = "^0.18.0" +borsh-construct = "^0.1.0" +websockets = ">=9.0,<11.0" +toolz = "^0.11.2" +jsonrpcclient = "^4.0.1" +zstandard = "^0.18.0" +pytest = "^7.2.0" +pytest-xprocess = "^0.18.1" +pytest-asyncio = "^0.21.0" +more-itertools = "^8.11.0" +pyheck = "^0.1.4" +typer = { version = "0.4.1", optional = true } +ipython = { version = "^8.0.1", optional = true } +genpy = { version = "^2021.1", optional = true } +black = { version = "^22.3.0", optional = true } +autoflake = { version = "^1.4", optional = true } +based58 = "^0.1.1" +anchorpy-core = "^0.1.3" +py = "^1.11.0" +toml = "^0.10.2" + +[tool.poetry.extras] +cli = ["typer", "ipython", "genpy", "black", "autoflake"] + + +[tool.poetry.scripts] +anchorpy = "anchorpy.cli:app" + +[tool.poetry.dev-dependencies] +mypy = "^0.982" +jinja2 = "3.0.3" +mkdocs-material = "^8.1.7" +bump2version = "^1.0.1" +mkdocstrings = "^0.17.0" +ruff = "^0.0.220" + +[tool.poetry.plugins.pytest11] +pytest_anchorpy = "anchorpy.pytest_plugin" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.ruff] +select = [ + "A", + "B", + "D", + "E", + "F", + "I", + "ARG", + "BLE", + "C4", + "SIM", + "PLC", + "PLE", + "PLR", + "PLW", + "RUF", +] +ignore = ["B023", "D100", "D101", "D102", "D103", "D104", "D107", "D203"] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# Assume Python 3.10. +target-version = "py310" + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.per-file-ignores] +"src/anchorpy/coder/*.py" = ["ARG002"] +"src/anchorpy/borsh_extension.py" = ["ARG002"] +"tests/**/*.py" = ["ARG001", "E501"] +"tests/client_gen/example_program_gen/**/*.py" = ["C417", "I001", "F401"] +"tests/client_gen/token/**/*.py" = ["C417", "I001", "F401"] +"src/anchorpy/cli.py" = ["B008"] + +[tool.pyright] +reportMissingModuleSource = false diff --git a/basics/anchorpy-main/pytest.ini b/basics/anchorpy-main/pytest.ini new file mode 100644 index 0000000..b8aa1e9 --- /dev/null +++ b/basics/anchorpy-main/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +markers = + unit: mark a test as a unit test. +addopts = -x +asyncio_mode=strict From df12bf63b83c6ee5b1164688aabb066667aa5cba Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:26:53 +0530 Subject: [PATCH 05/19] Delete basics/anchorpy-main/gh --- basics/anchorpy-main/gh | 1 - 1 file changed, 1 deletion(-) delete mode 100644 basics/anchorpy-main/gh diff --git a/basics/anchorpy-main/gh b/basics/anchorpy-main/gh deleted file mode 100644 index 8b13789..0000000 --- a/basics/anchorpy-main/gh +++ /dev/null @@ -1 +0,0 @@ - From 8f0c0226b98862d6b0e7c6b36b4f27945e4b0366 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:28:35 +0530 Subject: [PATCH 06/19] Update README.md --- basics/anchorpy-main/README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/basics/anchorpy-main/README.md b/basics/anchorpy-main/README.md index 904aad7..aceffbb 100644 --- a/basics/anchorpy-main/README.md +++ b/basics/anchorpy-main/README.md @@ -1,11 +1,5 @@ # AnchorPy -
- -
---- - -[![Discord Chat](https://img.shields.io/discord/889577356681945098?color=blueviolet)](https://discord.gg/sxy4zxBckh) AnchorPy is the gateway to interacting with [Anchor](https://github.com/project-serum/anchor) programs in Python. It provides: @@ -15,7 +9,6 @@ It provides: - A Pytest plugin - A CLI with various utilities for Anchor Python development. -Read the [Documentation](https://kevinheavey.github.io/anchorpy/). From 54a415dd0240318cb44768653b756b30f3bcc6ec Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:29:10 +0530 Subject: [PATCH 07/19] Create bh --- basics/anchorpy-main/tests/bh | 1 + 1 file changed, 1 insertion(+) create mode 100644 basics/anchorpy-main/tests/bh diff --git a/basics/anchorpy-main/tests/bh b/basics/anchorpy-main/tests/bh new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basics/anchorpy-main/tests/bh @@ -0,0 +1 @@ + From f0a7700e3c7ee28d7fed5e870cb811081a70144a Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:29:49 +0530 Subject: [PATCH 08/19] Add files via upload --- basics/anchorpy-main/tests/__init__.py | 0 basics/anchorpy-main/tests/conftest.py | 38 + basics/anchorpy-main/tests/test_basic_0.py | 22 + basics/anchorpy-main/tests/test_basic_1.py | 61 ++ basics/anchorpy-main/tests/test_basic_2.py | 73 ++ basics/anchorpy-main/tests/test_basic_3.py | 49 + .../tests/test_basic_3_bankrun.py | 63 ++ basics/anchorpy-main/tests/test_chat.py | 130 +++ basics/anchorpy-main/tests/test_cli.py | 29 + basics/anchorpy-main/tests/test_cli_init.py | 25 + basics/anchorpy-main/tests/test_composite.py | 70 ++ basics/anchorpy-main/tests/test_errors.py | 139 +++ basics/anchorpy-main/tests/test_events.py | 43 + basics/anchorpy-main/tests/test_misc.py | 234 +++++ basics/anchorpy-main/tests/test_misc_core.py | 686 +++++++++++++ .../tests/test_misc_core_bankrun.py | 938 ++++++++++++++++++ basics/anchorpy-main/tests/test_multisig.py | 192 ++++ basics/anchorpy-main/tests/test_pyth.py | 135 +++ basics/anchorpy-main/tests/test_zero_copy.py | 334 +++++++ 19 files changed, 3261 insertions(+) create mode 100644 basics/anchorpy-main/tests/__init__.py create mode 100644 basics/anchorpy-main/tests/conftest.py create mode 100644 basics/anchorpy-main/tests/test_basic_0.py create mode 100644 basics/anchorpy-main/tests/test_basic_1.py create mode 100644 basics/anchorpy-main/tests/test_basic_2.py create mode 100644 basics/anchorpy-main/tests/test_basic_3.py create mode 100644 basics/anchorpy-main/tests/test_basic_3_bankrun.py create mode 100644 basics/anchorpy-main/tests/test_chat.py create mode 100644 basics/anchorpy-main/tests/test_cli.py create mode 100644 basics/anchorpy-main/tests/test_cli_init.py create mode 100644 basics/anchorpy-main/tests/test_composite.py create mode 100644 basics/anchorpy-main/tests/test_errors.py create mode 100644 basics/anchorpy-main/tests/test_events.py create mode 100644 basics/anchorpy-main/tests/test_misc.py create mode 100644 basics/anchorpy-main/tests/test_misc_core.py create mode 100644 basics/anchorpy-main/tests/test_misc_core_bankrun.py create mode 100644 basics/anchorpy-main/tests/test_multisig.py create mode 100644 basics/anchorpy-main/tests/test_pyth.py create mode 100644 basics/anchorpy-main/tests/test_zero_copy.py diff --git a/basics/anchorpy-main/tests/__init__.py b/basics/anchorpy-main/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/tests/conftest.py b/basics/anchorpy-main/tests/conftest.py new file mode 100644 index 0000000..7030338 --- /dev/null +++ b/basics/anchorpy-main/tests/conftest.py @@ -0,0 +1,38 @@ +"""Pytest config.""" +import asyncio +import subprocess +from pathlib import Path + +from pytest import fixture + +# Since our other fixtures have module scope, we need to define +# this event_loop fixture and give it module scope otherwise +# pytest-asyncio will break. + + +@fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for each test case.""" + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() + + +@fixture(scope="session") +def project_parent_dir(tmpdir_factory) -> Path: + return Path(tmpdir_factory.mktemp("temp")) + + +@fixture(scope="session") +def project_dir(project_parent_dir: Path) -> Path: + proj_dir = project_parent_dir / "tmp" + command = ( + f"anchorpy client-gen tests/idls/clientgen_example_program.json {proj_dir} " + "--program-id 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 --pdas" + ) + subprocess.run( + command, + shell=True, + check=True, + ) + return proj_dir diff --git a/basics/anchorpy-main/tests/test_basic_0.py b/basics/anchorpy-main/tests/test_basic_0.py new file mode 100644 index 0000000..6d176ad --- /dev/null +++ b/basics/anchorpy-main/tests/test_basic_0.py @@ -0,0 +1,22 @@ +"""Mimics anchor/examples/tutorial/basic-0/tests/basic-0.js.""" +from pathlib import Path + +from anchorpy import create_workspace +from anchorpy.pytest_plugin import localnet_fixture +from pytest import mark + +PATH = Path("anchor/examples/tutorial/basic-0") + +# We use localnet_fixture here to make sure it works, but use +# workspace_fixture elsewhere. +localnet = localnet_fixture(PATH) + + +@mark.asyncio +async def test_init(localnet) -> None: + """Test that the initialize function is invoked successfully.""" + workspace = create_workspace(PATH) + program = workspace["basic_0"] + res = await program.rpc["initialize"]() + assert res + await program.close() diff --git a/basics/anchorpy-main/tests/test_basic_1.py b/basics/anchorpy-main/tests/test_basic_1.py new file mode 100644 index 0000000..43be062 --- /dev/null +++ b/basics/anchorpy-main/tests/test_basic_1.py @@ -0,0 +1,61 @@ +"""Mimics anchor/examples/tutorial/basic-1.""" + +from anchorpy import Context, Program +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + +workspace = workspace_fixture( + "anchor/examples/tutorial/basic-1", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["basic_1"] + + +@async_fixture(scope="module") +async def initialized_account(program: Program) -> Keypair: + """Generate a keypair and initialize it.""" + my_account = Keypair() + await program.rpc["initialize"]( + 1234, + ctx=Context( + accounts={ + "my_account": my_account.pubkey(), + "user": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[my_account], + ), + ) + return my_account + + +@mark.asyncio +async def test_create_and_initialize_account( + program: Program, + initialized_account: Keypair, +) -> None: + """Test creating and initializing account in single tx.""" + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 1234 + + +@mark.asyncio +async def test_update_previously_created_account( + initialized_account: Keypair, + program: Program, +) -> None: + """Test updating a previously created account.""" + await program.rpc["update"]( + 4321, + ctx=Context(accounts={"my_account": initialized_account.pubkey()}), + ) + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 4321 diff --git a/basics/anchorpy-main/tests/test_basic_2.py b/basics/anchorpy-main/tests/test_basic_2.py new file mode 100644 index 0000000..0524633 --- /dev/null +++ b/basics/anchorpy-main/tests/test_basic_2.py @@ -0,0 +1,73 @@ +"""Mimics anchor/examples/tutorial/basic-2/tests/basic-2.js.""" + +from anchorpy import Context, Program, Provider +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + +workspace = workspace_fixture("anchor/examples/tutorial/basic-2") + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["basic_2"] + + +@fixture(scope="module") +def provider(program: Program) -> Provider: + """Get a Provider instance.""" + return program.provider + + +@async_fixture(scope="module") +async def created_counter(program: Program, provider: Provider) -> Keypair: + """Create the counter.""" + counter = Keypair() + await program.rpc["create"]( + provider.wallet.public_key, + ctx=Context( + accounts={ + "counter": counter.pubkey(), + "user": provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[counter], + ), + ) + return counter + + +@mark.asyncio +async def test_create_counter( + created_counter: Keypair, + program: Program, + provider: Provider, +) -> None: + """Test creating a counter.""" + counter_account = await program.account["Counter"].fetch(created_counter.pubkey()) + assert counter_account.authority == provider.wallet.public_key + assert counter_account.count == 0 + + +@mark.asyncio +async def test_update_counter( + created_counter: Keypair, + program: Program, + provider: Provider, +) -> None: + """Test updating the counter.""" + await program.rpc["increment"]( + ctx=Context( + accounts={ + "counter": created_counter.pubkey(), + "authority": provider.wallet.public_key, + }, + ), + ) + counter_account = await program.account["Counter"].fetch(created_counter.pubkey()) + assert counter_account.authority == provider.wallet.public_key + assert counter_account.count == 1 diff --git a/basics/anchorpy-main/tests/test_basic_3.py b/basics/anchorpy-main/tests/test_basic_3.py new file mode 100644 index 0000000..b87b089 --- /dev/null +++ b/basics/anchorpy-main/tests/test_basic_3.py @@ -0,0 +1,49 @@ +"""Mimics anchor/examples/tutorial/basic-3/tests/basic-3.js.""" +from typing import AsyncGenerator + +from anchorpy import Context, Provider +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import mark +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + +workspace = workspace_fixture("anchor/examples/tutorial/basic-3") + + +@async_fixture(scope="module") +async def provider() -> AsyncGenerator[Provider, None]: + """Create a Provider instance.""" + prov = Provider.local() + yield prov + await prov.close() + + +@mark.asyncio +async def test_cpi(workspace: WorkspaceType, provider: Provider) -> None: + """Test CPI from puppet master to puppet.""" + puppet_master = workspace["puppet_master"] + puppet = workspace["puppet"] + new_puppet_account = Keypair() + await puppet.rpc["initialize"]( + ctx=Context( + accounts={ + "puppet": new_puppet_account.pubkey(), + "user": provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[new_puppet_account], + ), + ) + await puppet_master.rpc["pull_strings"]( + 111, + ctx=Context( + accounts={ + "puppet": new_puppet_account.pubkey(), + "puppet_program": puppet.program_id, + }, + ), + ) + puppet_account = await puppet.account["Data"].fetch(new_puppet_account.pubkey()) + assert puppet_account.data == 111 diff --git a/basics/anchorpy-main/tests/test_basic_3_bankrun.py b/basics/anchorpy-main/tests/test_basic_3_bankrun.py new file mode 100644 index 0000000..2ba38a1 --- /dev/null +++ b/basics/anchorpy-main/tests/test_basic_3_bankrun.py @@ -0,0 +1,63 @@ +"""Mimics anchor/examples/tutorial/basic-3/tests/basic-3.js.""" +from pathlib import Path +from typing import AsyncGenerator + +from anchorpy import Context +from anchorpy.pytest_plugin import bankrun_fixture +from anchorpy.workspace import WorkspaceType, close_workspace, create_workspace +from pytest import mark +from pytest_asyncio import fixture as async_fixture +from solders.bankrun import ProgramTestContext +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + +PATH = Path("anchor/examples/tutorial/basic-3") + +bankrun = bankrun_fixture(PATH) + + +@async_fixture(scope="module") +async def workspace(bankrun: ProgramTestContext) -> AsyncGenerator[WorkspaceType, None]: + ws = create_workspace(PATH) + yield ws + await close_workspace(ws) + + +@mark.asyncio +async def test_cpi(workspace: WorkspaceType, bankrun: ProgramTestContext) -> None: + """Test CPI from puppet master to puppet.""" + puppet_master = workspace["puppet_master"] + puppet = workspace["puppet"] + new_puppet_account = Keypair() + payer = bankrun.payer + blockhash = bankrun.last_blockhash + client = bankrun.banks_client + tx0 = puppet.transaction["initialize"]( + payer=payer, + blockhash=blockhash, + ctx=Context( + accounts={ + "puppet": new_puppet_account.pubkey(), + "user": payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[new_puppet_account], + ), + ) + await client.process_transaction(tx0) + tx1 = puppet_master.transaction["pull_strings"]( + 111, + payer=payer, + blockhash=blockhash, + ctx=Context( + accounts={ + "puppet": new_puppet_account.pubkey(), + "puppet_program": puppet.program_id, + }, + ), + ) + await client.process_transaction(tx1) + puppet_account_raw = await client.get_account(new_puppet_account.pubkey()) + assert puppet_account_raw is not None + decoded = puppet.account["Data"].coder.accounts.decode(puppet_account_raw.data) + assert decoded.data == 111 diff --git a/basics/anchorpy-main/tests/test_chat.py b/basics/anchorpy-main/tests/test_chat.py new file mode 100644 index 0000000..8af7c17 --- /dev/null +++ b/basics/anchorpy-main/tests/test_chat.py @@ -0,0 +1,130 @@ +import random +import string + +from anchorpy import Context, Program, Provider +from anchorpy.pytest_plugin import workspace_fixture +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.sysvar import RENT + +workspace = workspace_fixture( + "anchor/tests/chat/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace) -> Program: + """Create a Program instance.""" + return workspace["chat"] + + +@fixture(scope="module") +async def provider(program: Program) -> Provider: + """Get a Provider instance.""" + return program.provider + + +@async_fixture(scope="module") +async def created_chatroom(program: Program) -> Keypair: + chatroom = Keypair() + await program.rpc["create_chat_room"]( + "Test Chat", + ctx=Context( + accounts={ + "chat_room": chatroom.pubkey(), + "rent": RENT, + }, + pre_instructions=[ + await program.account["ChatRoom"].create_instruction(chatroom), + ], + signers=[chatroom], + ), + ) + return chatroom + + +@async_fixture(scope="module") +async def created_user( + created_chatroom: Keypair, program: Program +) -> tuple[Pubkey, Pubkey]: + authority = program.provider.wallet.public_key + user, bump = Pubkey.find_program_address([bytes(authority)], program.program_id) + await program.rpc["create_user"]( + "My User", + ctx=Context( + accounts={ + "user": user, + "authority": authority, + "system_program": SYS_PROGRAM_ID, + } + ), + ) + return user, authority + + +@async_fixture(scope="module") +async def sent_messages( + created_user: tuple[Pubkey, Pubkey], + created_chatroom: Keypair, + program: Program, +) -> list[str]: + user, authority = created_user + num_messages = 10 + to_choose = string.ascii_uppercase + string.digits + messages = ["".join(random.choices(to_choose, k=13)) for _ in range(num_messages)] + for i, msg in enumerate(messages): + print(f"sending message {i}") + await program.rpc["send_message"]( + msg, + ctx=Context( + accounts={ + "user": user, + "authority": authority, + "chat_room": created_chatroom.pubkey(), + }, + ), + ) + return messages + + +@mark.asyncio +async def test_created_chatroom(created_chatroom: Keypair, program: Program) -> None: + chat = await program.account["ChatRoom"].fetch(created_chatroom.pubkey()) + name = bytes(chat.name).rstrip(b"\x00").decode("utf-8") + assert name == "Test Chat" + assert len(chat.messages) == 33607 + assert chat.head == 0 + assert chat.tail == 0 + + +@mark.asyncio +async def test_created_user( + created_user: tuple[Pubkey, Pubkey], + program: Program, +) -> None: + user, authority = created_user + account = await program.account["User"].fetch(user) + assert account.name == "My User" + assert account.authority == authority + + +@mark.asyncio +async def test_sent_messages( + program: Program, + created_chatroom: Keypair, + sent_messages: list[str], + created_user: tuple[Pubkey, Pubkey], +) -> None: + user, _ = created_user + chat = await program.account["ChatRoom"].fetch(created_chatroom.pubkey()) + for i, msg in enumerate(chat.messages): + if i < len(sent_messages): + data = bytes(msg.data).rstrip(b"\x00").decode("utf-8") + print(f"Message {data}") + assert msg.from_ == user + assert data == sent_messages[i] + else: + assert msg.data == [0] * 280 diff --git a/basics/anchorpy-main/tests/test_cli.py b/basics/anchorpy-main/tests/test_cli.py new file mode 100644 index 0000000..7e964de --- /dev/null +++ b/basics/anchorpy-main/tests/test_cli.py @@ -0,0 +1,29 @@ +"""Test that the CLI commands work.""" + +from pathlib import Path + +from anchorpy import localnet_fixture +from anchorpy.cli import app +from solana.rpc.api import Client +from solana.rpc.commitment import Processed +from solders.signature import Signature +from typer.testing import CliRunner + +PATH = Path("anchor/examples/tutorial/basic-0") + +localnet = localnet_fixture(PATH) + +runner = CliRunner() + + +def test_shell(localnet, monkeypatch) -> None: + monkeypatch.chdir("anchor/examples/tutorial/basic-0") + cli_input = "await workspace['basic_0'].rpc['initialize']()\nexit()" + result = runner.invoke(app, ["shell"], input=cli_input) + assert result.exit_code == 0 + assert "Hint: type `workspace`" in result.stdout + tx_sig = Signature.from_string( + result.stdout.split("Out[1]: \n")[1].split("\n ")[1].split(",")[0] + ) + client = Client() + client.confirm_transaction(tx_sig, commitment=Processed) diff --git a/basics/anchorpy-main/tests/test_cli_init.py b/basics/anchorpy-main/tests/test_cli_init.py new file mode 100644 index 0000000..84aed56 --- /dev/null +++ b/basics/anchorpy-main/tests/test_cli_init.py @@ -0,0 +1,25 @@ +import subprocess +from pathlib import Path + +from pytest import fixture + +PATH = Path("anchor/examples/tutorial/basic-0") +PROJECT_NAME = "foo" + + +@fixture(scope="session") +def project_parent_dir(tmpdir_factory) -> Path: + return Path(tmpdir_factory.mktemp("temp")) + + +@fixture(scope="session") +def project_dir(project_parent_dir: Path) -> Path: + subprocess.run(["anchor", "init", PROJECT_NAME], cwd=project_parent_dir, check=True) + proj_dir = project_parent_dir / PROJECT_NAME + subprocess.run(["anchor", "build"], cwd=proj_dir, check=True) + subprocess.run(["anchorpy", "init", PROJECT_NAME], cwd=proj_dir, check=True) + return proj_dir + + +def test_init(project_dir: Path): + subprocess.run("pytest", cwd=project_dir, check=True) diff --git a/basics/anchorpy-main/tests/test_composite.py b/basics/anchorpy-main/tests/test_composite.py new file mode 100644 index 0000000..a1e70ce --- /dev/null +++ b/basics/anchorpy-main/tests/test_composite.py @@ -0,0 +1,70 @@ +"""Mimics anchor/tests/composite/tests/composite.js.""" +from anchorpy import Context, Program +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.sysvar import RENT + +workspace = workspace_fixture( + "anchor/tests/composite/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["composite"] + + +@async_fixture(scope="module") +async def initialized_accounts(program: Program) -> tuple[Keypair, Keypair]: + """Generate keypairs and use them when callling the initialize function.""" + dummy_a = Keypair() + dummy_b = Keypair() + await program.rpc["initialize"]( + ctx=Context( + accounts={ + "dummy_a": dummy_a.pubkey(), + "dummy_b": dummy_b.pubkey(), + "rent": RENT, + }, + signers=[dummy_a, dummy_b], + pre_instructions=[ + await program.account["DummyA"].create_instruction(dummy_a), + await program.account["DummyB"].create_instruction(dummy_b), + ], + ), + ) + return dummy_a, dummy_b + + +@async_fixture(scope="module") +async def composite_updated_accounts( + program: Program, + initialized_accounts: tuple[Keypair, Keypair], +) -> tuple[Keypair, Keypair]: + """Run composite_update and return the keypairs used.""" + dummy_a, dummy_b = initialized_accounts + ctx = Context( + accounts={ + "foo": {"dummy_a": dummy_a.pubkey()}, + "bar": {"dummy_b": dummy_b.pubkey()}, + }, + ) + await program.rpc["composite_update"](1234, 4321, ctx=ctx) + return initialized_accounts + + +@mark.asyncio +async def test_composite_update( + program: Program, + composite_updated_accounts: tuple[Keypair, Keypair], +) -> None: + """Test that the call to composite_update worked.""" + dummy_a, dummy_b = composite_updated_accounts + dummy_a_account = await program.account["DummyA"].fetch(dummy_a.pubkey()) + dummy_b_account = await program.account["DummyB"].fetch(dummy_b.pubkey()) + assert dummy_a_account.data == 1234 + assert dummy_b_account.data == 4321 diff --git a/basics/anchorpy-main/tests/test_errors.py b/basics/anchorpy-main/tests/test_errors.py new file mode 100644 index 0000000..5ab5721 --- /dev/null +++ b/basics/anchorpy-main/tests/test_errors.py @@ -0,0 +1,139 @@ +"""Mimics anchor/tests/errors/tests/errors.js.""" +from anchorpy import Context, Program +from anchorpy.error import ProgramError +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark, raises +from solana.rpc.core import RPCNoResultException +from solana.transaction import Transaction +from solders.instruction import AccountMeta, Instruction +from solders.keypair import Keypair +from solders.sysvar import RENT + +workspace = workspace_fixture( + "anchor/tests/errors/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["errors"] + + +@mark.asyncio +async def test_hello_err(program: Program) -> None: + """Test error from hello func.""" + with raises(ProgramError) as excinfo: + await program.rpc["hello"]() + assert excinfo.value.code == 6000 + expected_msg = "This is an error message clients will automatically display" + assert excinfo.value.msg == expected_msg + assert expected_msg in str(excinfo) + assert excinfo.value.logs + + +@mark.asyncio +async def test_hello_no_msg_err(program: Program) -> None: + """Test error from helloNoMsg func.""" + with raises(ProgramError) as excinfo: + await program.rpc["hello_no_msg"]() + assert excinfo.value.msg == "HelloNoMsg" + assert excinfo.value.code == 6000 + 123 + assert excinfo.value.logs + + +@mark.asyncio +async def test_hello_next_err(program: Program) -> None: + """Test error from helloNext func.""" + with raises(ProgramError) as excinfo: + await program.rpc["hello_next"]() + assert excinfo.value.msg == "HelloNext" + assert excinfo.value.code == 6000 + 124 + assert excinfo.value.logs + + +@mark.asyncio +async def test_mut_err(program: Program) -> None: + """Test mmut error.""" + with raises(ProgramError) as excinfo: + await program.rpc["mut_error"](ctx=Context(accounts={"my_account": RENT})) + assert excinfo.value.msg == "A mut constraint was violated" + assert excinfo.value.code == 2000 + assert excinfo.value.logs + + +@mark.asyncio +async def test_has_one_err(program: Program) -> None: + """Test hasOneError.""" + account = Keypair() + with raises(ProgramError) as excinfo: + await program.rpc["has_one_error"]( + ctx=Context( + accounts={ + "my_account": account.pubkey(), + "owner": RENT, + "rent": RENT, + }, + pre_instructions=[ + await program.account["HasOneAccount"].create_instruction(account) + ], + signers=[account], + ) + ) + assert excinfo.value.msg == "A has_one constraint was violated" + assert excinfo.value.code == 2001 + assert excinfo.value.logs + + +@mark.asyncio +async def test_signer_err(program: Program) -> None: + """Test signer error.""" + tx = Transaction() + tx.add( + Instruction( + accounts=[ + AccountMeta( + pubkey=RENT, + is_writable=False, + is_signer=False, + ), + ], + program_id=program.program_id, + data=program.coder.instruction.encode("signer_error", {}), + ), + ) + with raises(RPCNoResultException): + await program.provider.send(tx) + + +@mark.asyncio +async def test_raw_custom_err(program: Program) -> None: + with raises(ProgramError) as excinfo: + await program.rpc["raw_custom_error"]( + ctx=Context( + accounts={ + "my_account": RENT, + }, + ) + ) + assert excinfo.value.msg == "HelloCustom" + assert excinfo.value.code == 6000 + 125 + assert excinfo.value.logs + + +@mark.asyncio +async def test_account_not_initialised_err(program: Program) -> None: + with raises(ProgramError) as excinfo: + await program.rpc["account_not_initialized_error"]( + ctx=Context( + accounts={ + "not_initialized_account": Keypair().pubkey(), + }, + ) + ) + assert ( + excinfo.value.msg + == "The program expected this account to be already initialized" + ) + assert excinfo.value.code == 3012 + assert excinfo.value.logs diff --git a/basics/anchorpy-main/tests/test_events.py b/basics/anchorpy-main/tests/test_events.py new file mode 100644 index 0000000..3cf25dd --- /dev/null +++ b/basics/anchorpy-main/tests/test_events.py @@ -0,0 +1,43 @@ +"""Mimics anchor/tests/errors/tests/events.js. + +Note: this is unfinished. +""" +from typing import cast + +from anchorpy import ( + EventParser, + Program, +) +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark +from solana.rpc.websocket_api import SolanaWsClientProtocol, connect +from solders.rpc.config import RpcTransactionLogsFilter +from solders.rpc.responses import LogsNotification + +workspace = workspace_fixture( + "anchor/tests/events/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["events"] + + +@mark.asyncio +async def test_initialize(program: Program) -> None: + async with cast(SolanaWsClientProtocol, connect()) as websocket: # type: ignore + await websocket.logs_subscribe(RpcTransactionLogsFilter.All) + await websocket.recv() + await program.rpc["initialize"]() + received = await websocket.recv() + first = received[0] + assert isinstance(first, LogsNotification) + logs = first.result.value.logs + parser = EventParser(program.program_id, program.coder) + parsed = [] + parser.parse_logs(logs, lambda evt: parsed.append(evt)) + event = parsed[0] + assert event.data.data == 5 + assert event.data.label == "hello" diff --git a/basics/anchorpy-main/tests/test_misc.py b/basics/anchorpy-main/tests/test_misc.py new file mode 100644 index 0000000..56a5458 --- /dev/null +++ b/basics/anchorpy-main/tests/test_misc.py @@ -0,0 +1,234 @@ +"""Mimics anchor/tests/misc/tests/misc.js.""" +import asyncio +import subprocess +from pathlib import Path + +from anchorpy import Context, Program +from anchorpy.error import ProgramError +from anchorpy.provider import Provider, Wallet +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.utils.rpc import invoke +from anchorpy.workspace import WorkspaceType +from anchorpy_core.idl import IdlConst, IdlTypeSimple +from pytest import fixture, mark, raises +from pytest_asyncio import fixture as async_fixture +from solana.rpc.core import RPCNoResultException +from solana.rpc.types import MemcmpOpts +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.sysvar import RENT + +PATH = Path("anchor/tests/misc/") +# bankrun = bankrun_fixture(PATH, build_cmd="anchor build --skip-lint") +workspace = workspace_fixture(PATH, build_cmd="anchor build --skip-lint") + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["misc"] + + +def test_idl_constants(program: Program) -> None: + idl_constants = program.idl.constants + assert idl_constants == [ + IdlConst(name="BASE", ty=IdlTypeSimple.U128, value="1_000_000"), + IdlConst(name="DECIMALS", ty=IdlTypeSimple.U8, value="6"), + ] + + +def test_methods(program: Program, initialized_keypair: Keypair) -> None: + ix_from_methods = ( + program.methods["test_close"] + .accounts( + { + "data": initialized_keypair.pubkey(), + "sol_dest": initialized_keypair.pubkey(), + } + ) + .instruction() + ) + ix_legacy = program.instruction["test_close"]( + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "sol_dest": initialized_keypair.pubkey(), + }, + ) + ) + assert ix_from_methods == ix_legacy + + +@mark.asyncio +async def test_at_constructor(program: Program) -> None: + """Test that the Program.at classmethod works.""" + idl_path = "target/idl/misc.json" + subprocess.run( + ["anchor", "idl", "init", "-f", idl_path, str(program.program_id)], + cwd=PATH, + ) + fetched = await program.at(program.program_id, program.provider) + await fetched.close() + assert fetched.idl.name == "misc" + + +@async_fixture(scope="module") +async def initialized_keypair(program: Program) -> Keypair: + data = Keypair() + await program.rpc["initialize"]( + 1234, + 22, + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + signers=[data], + pre_instructions=[await program.account["Data"].create_instruction(data)], + ), + ) + return data + + +@mark.asyncio +async def test_readonly_provider( + program: Program, initialized_keypair: Keypair +) -> None: + async with Provider.readonly() as provider: + readonly_program = Program(program.idl, program.program_id, provider=provider) + data_account = await readonly_program.account["Data"].fetch( + initialized_keypair.pubkey() + ) + assert data_account.udata == 1234 + assert data_account.idata == 22 + + +@mark.asyncio +async def test_fetch_multiple(program: Program, initialized_keypair: Keypair) -> None: + batch_size = 2 + n_accounts = batch_size * 100 + 1 + data_account = await program.account["Data"].fetch(initialized_keypair.pubkey()) + pubkeys = [initialized_keypair.pubkey()] * n_accounts + data_accounts = await program.account["Data"].fetch_multiple( + pubkeys, batch_size=batch_size + ) + assert len(data_accounts) == n_accounts + assert all(acc == data_account for acc in data_accounts) + + +@mark.asyncio +async def test_can_use_executable_attribute(program: Program) -> None: + await program.rpc["test_executable"]( + ctx=Context(accounts={"program": program.program_id}), + ) + # sleep so we don't get AlreadyProcessed error + await asyncio.sleep(10) + with raises(ProgramError): + await program.rpc["test_executable"]( + ctx=Context(accounts={"program": program.provider.wallet.public_key}), + ) + + +@mark.asyncio +async def test_can_execute_fallback_function(program: Program) -> None: + with raises(RPCNoResultException) as excinfo: + await invoke(program.program_id, program.provider) + assert ( + "Transaction failed to sanitize accounts offsets correctly" + in excinfo.value.args[0] + ) + + +@mark.asyncio +async def test_can_fetch_all_accounts_of_a_given_type( + program: Program, event_loop +) -> None: + # Initialize the accounts. + data1 = Keypair() + data2 = Keypair() + data3 = Keypair() + data4 = Keypair() + # Initialize filterable data. + filterable1 = Keypair().pubkey() + filterable2 = Keypair().pubkey() + provider = Provider( + program.provider.connection, + Wallet(Keypair()), + program.provider.opts, + ) + another_program = Program(program.idl, program.program_id, provider) + lamports_per_sol = 1000000000 + tx_res = await program.provider.connection.request_airdrop( + another_program.provider.wallet.public_key, + lamports_per_sol, + ) + signature = tx_res.value + await program.provider.connection.confirm_transaction(signature) + # Create all the accounts. + tasks = [ + program.rpc["test_fetch_all"]( + filterable1, + ctx=Context( + accounts={ + "data": data1.pubkey(), + "authority": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data1], + ), + ), + program.rpc["test_fetch_all"]( + filterable1, + ctx=Context( + accounts={ + "data": data2.pubkey(), + "authority": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data2], + ), + ), + program.rpc["test_fetch_all"]( + filterable2, + ctx=Context( + accounts={ + "data": data3.pubkey(), + "authority": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data3], + ), + ), + another_program.rpc["test_fetch_all"]( + filterable1, + ctx=Context( + accounts={ + "data": data4.pubkey(), + "authority": another_program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data4], + ), + ), + ] + await asyncio.wait([event_loop.create_task(t) for t in tasks]) + all_accounts = await program.account["DataWithFilter"].all() + all_accounts_filtered_by_bytes = await program.account["DataWithFilter"].all( + bytes(program.provider.wallet.public_key), + ) + all_accounts_filtered_by_program_filters1 = await program.account[ + "DataWithFilter" + ].all( + filters=[ + MemcmpOpts(offset=8, bytes=str(program.provider.wallet.public_key)), + MemcmpOpts(offset=40, bytes=str(filterable1)), + ], + ) + all_accounts_filtered_by_program_filters2 = await program.account[ + "DataWithFilter" + ].all( + filters=[ + MemcmpOpts(offset=8, bytes=str(program.provider.wallet.public_key)), + MemcmpOpts(offset=40, bytes=str(filterable2)), + ], + ) + assert len(all_accounts) == 4 + assert len(all_accounts_filtered_by_bytes) == 3 + assert len(all_accounts_filtered_by_program_filters1) == 2 + assert len(all_accounts_filtered_by_program_filters2) == 1 diff --git a/basics/anchorpy-main/tests/test_misc_core.py b/basics/anchorpy-main/tests/test_misc_core.py new file mode 100644 index 0000000..cb09d1e --- /dev/null +++ b/basics/anchorpy-main/tests/test_misc_core.py @@ -0,0 +1,686 @@ +"""Mimics anchor/tests/misc/tests/misc.js.""" +from pathlib import Path + +from anchorpy import Context, Program +from anchorpy.error import ProgramError +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark, raises +from pytest_asyncio import fixture as async_fixture +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.system_program import TransferParams, transfer +from solders.sysvar import RENT +from spl.token.async_client import AsyncToken +from spl.token.constants import TOKEN_PROGRAM_ID + +PATH = Path("anchor/tests/misc/") +workspace = workspace_fixture(PATH, build_cmd="anchor build --skip-lint") + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["misc"] + + +@fixture(scope="module") +def misc2program(workspace: WorkspaceType) -> Program: + return workspace["misc2"] + + +@async_fixture(scope="module") +async def initialized_keypair(program: Program) -> Keypair: + data = Keypair() + await program.rpc["initialize"]( + 1234, + 22, + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + signers=[data], + pre_instructions=[await program.account["Data"].create_instruction(data)], + ), + ) + return data + + +@mark.asyncio +async def test_can_use_u128_and_i128( + program: Program, initialized_keypair: Keypair +) -> None: + data_account = await program.account["Data"].fetch(initialized_keypair.pubkey()) + assert data_account.udata == 1234 + assert data_account.idata == 22 + + +@async_fixture(scope="module") +async def keypair_after_test_u16(program: Program) -> Keypair: + data = Keypair() + await program.rpc["test_u16"]( + 99, + ctx=Context( + accounts={"my_account": data.pubkey(), "rent": RENT}, + signers=[data], + pre_instructions=[ + await program.account["DataU16"].create_instruction(data) + ], + ), + ) + return data + + +@mark.asyncio +async def test_can_use_u16( + program: Program, + keypair_after_test_u16: Keypair, +) -> None: + data_account = await program.account["DataU16"].fetch( + keypair_after_test_u16.pubkey(), + ) + assert data_account.data == 99 + + +@mark.asyncio +async def test_can_use_owner_constraint( + program: Program, initialized_keypair: Keypair +) -> None: + await program.rpc["test_owner"]( + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "misc": program.program_id, + }, + ), + ) + with raises(ProgramError): + await program.rpc["test_owner"]( + ctx=Context( + accounts={ + "data": program.provider.wallet.public_key, + "misc": program.program_id, + }, + ), + ) + + +@mark.asyncio +async def test_can_retrieve_events_when_simulating_transaction( + program: Program, +) -> None: + resp = await program.simulate["test_simulate"](44) + expected_raw_first_entry = ( + "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh invoke [1]" + ) + events = resp.events + assert resp.raw[0] == expected_raw_first_entry + assert events[0].name == "E1" + assert events[0].data.data == 44 + assert events[1].name == "E2" + assert events[1].data.data == 1234 + assert events[2].name == "E3" + assert events[2].data.data == 9 + + +@mark.asyncio +async def test_can_use_i8_in_idl(program: Program) -> None: + data = Keypair() + await program.rpc["test_i8"]( + -3, + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + pre_instructions=[await program.account["DataI8"].create_instruction(data)], + signers=[data], + ), + ) + data_account = await program.account["DataI8"].fetch(data.pubkey()) + assert data_account.data == -3 + + +@async_fixture(scope="module") +async def data_i16_keypair(program: Program) -> Keypair: + data = Keypair() + await program.rpc["test_i16"]( + -2048, + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + pre_instructions=[ + await program.account["DataI16"].create_instruction(data) + ], + signers=[data], + ), + ) + return data + + +@mark.asyncio +async def test_can_use_i16_in_idl(program: Program, data_i16_keypair: Keypair) -> None: + data_account = await program.account["DataI16"].fetch(data_i16_keypair.pubkey()) + assert data_account.data == -2048 + + +@mark.asyncio +async def test_fail_to_close_account_when_sending_lamports_to_itself( + program: Program, + initialized_keypair: Keypair, +) -> None: + with raises(ProgramError) as excinfo: + await program.rpc["test_close"]( + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "sol_dest": initialized_keypair.pubkey(), + }, + ), + ) + assert excinfo.value.code == 2011 + assert excinfo.value.msg == "A close constraint was violated" + + +@mark.asyncio +async def test_can_close_account( + program: Program, + initialized_keypair: Keypair, +) -> None: + open_account = await program.provider.connection.get_account_info( + initialized_keypair.pubkey(), + ) + assert open_account.value is not None + before_balance_raw = await program.provider.connection.get_account_info( + program.provider.wallet.public_key, + ) + before_balance_res = before_balance_raw.value + assert before_balance_res is not None + before_balance = before_balance_res.lamports + await program.rpc["test_close"]( + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "sol_dest": program.provider.wallet.public_key, + }, + ), + ) + after_balance_raw = await program.provider.connection.get_account_info( + program.provider.wallet.public_key, + ) + after_balance_res = after_balance_raw.value + assert after_balance_res is not None + after_balance = after_balance_res.lamports + assert after_balance > before_balance + closed_account = await program.provider.connection.get_account_info( + initialized_keypair.pubkey(), + ) + assert closed_account.value is None + + +@mark.asyncio +async def test_can_use_instruction_data_in_accounts_constraints( + program: Program, +) -> None: + seed = b"my-seed" + my_pda, nonce = Pubkey.find_program_address([seed, bytes(RENT)], program.program_id) + await program.rpc["test_instruction_constraint"]( + nonce, + ctx=Context(accounts={"my_pda": my_pda, "my_account": RENT}), + ) + + +@mark.asyncio +async def test_can_create_a_pda_with_instruction_data( + program: Program, +) -> None: + seed = bytes([1, 2, 3, 4]) + domain = "my-domain" + foo = RENT + my_pda, nonce = Pubkey.find_program_address( + [b"my-seed", domain.encode(), bytes(foo), seed], program.program_id + ) + await program.rpc["test_pda_init"]( + domain, + seed, + nonce, + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": program.provider.wallet.public_key, + "foo": foo, + "rent": RENT, + "system_program": SYS_PROGRAM_ID, + } + ), + ) + my_pda_account = await program.account["DataU16"].fetch(my_pda) + assert my_pda_account.data == 6 + + +@mark.asyncio +async def test_can_create_a_zero_copy_pda(program: Program) -> None: + my_pda, nonce = Pubkey.find_program_address([b"my-seed"], program.program_id) + await program.rpc["test_pda_init_zero_copy"]( + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": program.provider.wallet.public_key, + "rent": RENT, + "system_program": SYS_PROGRAM_ID, + }, + ), + ) + my_pda_account = await program.account["DataZeroCopy"].fetch(my_pda) + assert my_pda_account.data == 9 + assert my_pda_account.bump == nonce + + +@mark.asyncio +async def test_can_write_to_a_zero_copy_pda(program: Program) -> None: + my_pda, bump = Pubkey.find_program_address([b"my-seed"], program.program_id) + await program.rpc["test_pda_mut_zero_copy"]( + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": program.provider.wallet.public_key, + }, + ) + ) + my_pda_account = await program.account["DataZeroCopy"].fetch(my_pda) + assert my_pda_account.data == 1234 + assert my_pda_account.bump == bump + + +@mark.asyncio +async def test_can_create_a_token_account_from_seeds_pda(program: Program) -> None: + mint, mint_bump = Pubkey.find_program_address([b"my-mint-seed"], program.program_id) + my_pda, token_bump = Pubkey.find_program_address( + [b"my-token-seed"], program.program_id + ) + await program.rpc["test_token_seeds_init"]( + ctx=Context( + accounts={ + "my_pda": my_pda, + "mint": mint, + "authority": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "rent": RENT, + "token_program": TOKEN_PROGRAM_ID, + }, + ), + ) + mint_account = AsyncToken( + program.provider.connection, + mint, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + account = await mint_account.get_account_info(my_pda) + assert account.is_frozen is False + assert account.is_initialized is True + assert account.amount == 0 + assert account.owner == program.provider.wallet.public_key + assert account.mint == mint + + +@mark.asyncio +async def test_can_init_random_account(program: Program) -> None: + data = Keypair() + await program.rpc["test_init"]( + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + ), + ) + account = await program.account["DataI8"].fetch(data.pubkey()) + assert account.data == 3 + + +@mark.asyncio +async def test_can_init_random_account_prefunded(program: Program) -> None: + data = Keypair() + await program.rpc["test_init"]( + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=program.provider.wallet.public_key, + to_pubkey=data.pubkey(), + lamports=4039280, + ), + ), + ], + ), + ) + account = await program.account["DataI8"].fetch(data.pubkey()) + assert account.data == 3 + + +@mark.asyncio +async def test_can_init_random_zero_copy_account(program: Program) -> None: + data = Keypair() + await program.rpc["test_init_zero_copy"]( + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + ), + ) + account = await program.account["DataZeroCopy"].fetch(data.pubkey()) + assert account.data == 10 + assert account.bump == 2 + + +@mark.asyncio +async def test_can_create_random_mint_account( + program: Program, +) -> None: + mint = Keypair() + await program.rpc["test_init_mint"]( + ctx=Context( + accounts={ + "mint": mint.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[mint], + ), + ) + client = AsyncToken( + program.provider.connection, + mint.pubkey(), + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + mint_account = await client.get_mint_info() + assert mint_account.decimals == 6 + assert mint_account.mint_authority == program.provider.wallet.public_key + assert mint_account.freeze_authority == program.provider.wallet.public_key + + +@async_fixture(scope="module") +async def prefunded_mint(program: Program) -> Keypair: + mint = Keypair() + await program.rpc["test_init_mint"]( + ctx=Context( + accounts={ + "mint": mint.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[mint], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=program.provider.wallet.public_key, + to_pubkey=mint.pubkey(), + lamports=4039280, + ), + ), + ], + ), + ) + return mint + + +@mark.asyncio +async def test_can_create_random_mint_account_prefunded( + program: Program, + prefunded_mint: Keypair, +) -> None: + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + mint_account = await client.get_mint_info() + assert mint_account.decimals == 6 + assert mint_account.mint_authority == program.provider.wallet.public_key + + +@mark.asyncio +async def test_can_create_random_token_account( + program: Program, + prefunded_mint: Keypair, +) -> None: + token = Keypair() + await program.rpc["test_init_token"]( + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + ), + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + account = await client.get_account_info(token.pubkey()) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == program.provider.wallet.public_key + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_can_create_random_token_account_with_prefunding( + program: Program, + prefunded_mint: Keypair, +) -> None: + token = Keypair() + await program.rpc["test_init_token"]( + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=program.provider.wallet.public_key, + to_pubkey=token.pubkey(), + lamports=4039280, + ), + ) + ], + ), + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + account = await client.get_account_info(token.pubkey()) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == program.provider.wallet.public_key + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_can_create_random_token_account_with_prefunding_under_rent_exemption( + program: Program, + prefunded_mint: Keypair, +) -> None: + token = Keypair() + await program.rpc["test_init_token"]( + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=program.provider.wallet.public_key, + to_pubkey=token.pubkey(), + lamports=1, + ), + ) + ], + ), + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + program.provider.wallet.payer, + ) + account = await client.get_account_info(token.pubkey()) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == program.provider.wallet.public_key + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_init_multiple_accounts_via_composite_payer(program: Program) -> None: + data1 = Keypair() + data2 = Keypair() + await program.rpc["test_composite_payer"]( + ctx=Context( + accounts={ + "composite": { + "data": data1.pubkey(), + "payer": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + "data": data2.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[data1, data2], + ) + ) + account1 = await program.account["DataI8"].fetch(data1.pubkey()) + assert account1.data == 1 + + account2 = await program.account["Data"].fetch(data2.pubkey()) + assert account2.udata == 2 + assert account2.idata == 3 + + +@mark.asyncio +async def test_can_create_associated_token_account(program: Program) -> None: + # TODO + pass + + +@mark.asyncio +async def test_can_validate_associated_token_constraints(program: Program) -> None: + # TODO + pass + + +@mark.asyncio +async def test_can_use_pdas_with_empty_seeds(program: Program) -> None: + pda, bump = Pubkey.find_program_address([], program.program_id) + await program.rpc["test_init_with_empty_seeds"]( + ctx=Context( + accounts={ + "pda": pda, + "authority": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + ), + ) + await program.rpc["test_empty_seeds_constraint"]( + ctx=Context( + accounts={ + "pda": pda, + }, + ), + ) + pda2, _ = Pubkey.find_program_address( + [b"non-empty"], + program.program_id, + ) + with raises(ProgramError) as excinfo: + await program.rpc["test_empty_seeds_constraint"]( + ctx=Context( + accounts={ + "pda": pda2, + }, + ), + ) + assert excinfo.value.code == 2006 + + +@async_fixture(scope="module") +async def if_needed_acc(program: Program) -> Keypair: + keypair = Keypair() + await program.rpc["test_init_if_needed"]( + 1, + ctx=Context( + accounts={ + "data": keypair.pubkey(), + "system_program": SYS_PROGRAM_ID, + "payer": program.provider.wallet.public_key, + }, + signers=[keypair], + ), + ) + return keypair + + +@mark.asyncio +async def test_can_init_if_needed_a_new_account( + program: Program, + if_needed_acc: Keypair, +) -> None: + account = await program.account["DataU16"].fetch(if_needed_acc.pubkey()) + assert account.data == 1 + + +@mark.asyncio +async def test_can_init_if_needed_a_previously_created_account( + program: Program, + if_needed_acc: Keypair, +) -> None: + await program.rpc["test_init_if_needed"]( + 3, + ctx=Context( + accounts={ + "data": if_needed_acc.pubkey(), + "system_program": SYS_PROGRAM_ID, + "payer": program.provider.wallet.public_key, + }, + signers=[if_needed_acc], + ), + ) + account = await program.account["DataU16"].fetch(if_needed_acc.pubkey()) + assert account.data == 3 diff --git a/basics/anchorpy-main/tests/test_misc_core_bankrun.py b/basics/anchorpy-main/tests/test_misc_core_bankrun.py new file mode 100644 index 0000000..b86d927 --- /dev/null +++ b/basics/anchorpy-main/tests/test_misc_core_bankrun.py @@ -0,0 +1,938 @@ +"""Mimics anchor/tests/misc/tests/misc.js.""" +from pathlib import Path +from typing import Any, AsyncGenerator, Optional + +from anchorpy import Context, Program +from anchorpy.error import ProgramError +from anchorpy.program.context import EMPTY_CONTEXT +from anchorpy.program.core import _parse_idl_errors +from anchorpy.program.event import EventParser +from anchorpy.program.namespace.account import AccountClient +from anchorpy.program.namespace.simulate import SimulateResponse +from anchorpy.pytest_plugin import bankrun_fixture +from anchorpy.workspace import WorkspaceType, close_workspace, create_workspace +from pytest import fixture, mark, raises +from pytest_asyncio import fixture as async_fixture +from solana.rpc.core import RPCException +from solders.account import Account +from solders.bankrun import BanksClientError, ProgramTestContext +from solders.instruction import Instruction +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.rpc.responses import GetAccountInfoResp, RpcResponseContext +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.system_program import ( + CreateAccountParams, + TransferParams, + create_account, + transfer, +) +from solders.sysvar import RENT +from spl.token.async_client import AsyncToken +from spl.token.constants import TOKEN_PROGRAM_ID + +PATH = Path("anchor/tests/misc/") +bankrun = bankrun_fixture(PATH, build_cmd="anchor build --skip-lint") + + +@async_fixture(scope="module") +async def workspace(bankrun: ProgramTestContext) -> AsyncGenerator[WorkspaceType, None]: + ws = create_workspace(PATH) + yield ws + await close_workspace(ws) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["misc"] + + +@fixture(scope="module") +def misc2program(workspace: WorkspaceType) -> Program: + return workspace["misc2"] + + +async def bankrun_rpc( + prog: Program, name: str, args: list, ctx: Context, bankrun: ProgramTestContext +) -> None: + recent_blockhash = (await bankrun.banks_client.get_latest_blockhash())[0] + tx = prog.transaction[name]( + *args, payer=bankrun.payer, blockhash=recent_blockhash, ctx=ctx + ) + await bankrun.banks_client.process_transaction(tx) + + +def as_account_info_resp(acc: Optional[Account]) -> GetAccountInfoResp: + return GetAccountInfoResp(acc, RpcResponseContext(0)) + + +async def bankrun_create_instruction( + acc: AccountClient, signer: Keypair, program_id: Pubkey, bankrun: ProgramTestContext +) -> Instruction: + rent = await bankrun.banks_client.get_rent() + space = acc._size + mbre = rent.minimum_balance(space) + return create_account( + CreateAccountParams( + from_pubkey=bankrun.payer.pubkey(), + to_pubkey=signer.pubkey(), + space=space, + lamports=mbre, + owner=program_id, + ) + ) + + +async def bankrun_fetch( + acc: AccountClient, address: Pubkey, bankrun: ProgramTestContext +) -> Any: + raw = await bankrun.banks_client.get_account(address) + assert raw is not None + return acc.coder.accounts.decode(raw.data) + + +async def bankrun_simulate( + prog: Program, name: str, args: list, ctx: Context, bankrun: ProgramTestContext +) -> SimulateResponse: + blockhash = (await bankrun.banks_client.get_latest_blockhash())[0] + tx = prog.transaction[name]( + *args, payer=bankrun.payer, blockhash=blockhash, ctx=ctx + ) + resp = await bankrun.banks_client.simulate_transaction(tx) + resp_err = resp.result + meta = resp.meta + logs = [] if meta is None else meta.log_messages + idl_errors = _parse_idl_errors(prog.idl) + if resp_err is None: + events = [] + if prog.idl.events is not None: + parser = EventParser(prog.program_id, prog.coder) + parser.parse_logs(logs, lambda evt: events.append(evt)) + return SimulateResponse(events, logs) + else: + translated_err = ProgramError.parse_tx_error( + resp_err, idl_errors, prog.program_id, logs + ) + if translated_err is not None: + raise translated_err + raise RPCException(resp_err) + + +@async_fixture(scope="module") +async def initialized_keypair(program: Program, bankrun: ProgramTestContext) -> Keypair: + data = Keypair() + await bankrun_rpc( + program, + "initialize", + [ + 1234, + 22, + ], + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + signers=[data], + pre_instructions=[ + await bankrun_create_instruction( + program.account["Data"], data, program.program_id, bankrun + ) + ], + ), + bankrun=bankrun, + ) + return data + + +@mark.asyncio +async def test_can_use_u128_and_i128( + program: Program, initialized_keypair: Keypair, bankrun: ProgramTestContext +) -> None: + data_account = await bankrun_fetch( + program.account["Data"], initialized_keypair.pubkey(), bankrun + ) + assert data_account.udata == 1234 + assert data_account.idata == 22 + + +@async_fixture(scope="module") +async def keypair_after_test_u16( + program: Program, bankrun: ProgramTestContext +) -> Keypair: + data = Keypair() + await bankrun_rpc( + program, + "test_u16", + [99], + ctx=Context( + accounts={"my_account": data.pubkey(), "rent": RENT}, + signers=[data], + pre_instructions=[ + await bankrun_create_instruction( + program.account["DataU16"], data, program.program_id, bankrun + ) + ], + ), + bankrun=bankrun, + ) + return data + + +@mark.asyncio +async def test_can_use_u16( + program: Program, + keypair_after_test_u16: Keypair, + bankrun: ProgramTestContext, +) -> None: + data_account = await bankrun_fetch( + program.account["DataU16"], keypair_after_test_u16.pubkey(), bankrun + ) + assert data_account.data == 99 + + +@mark.asyncio +async def test_can_use_owner_constraint( + program: Program, initialized_keypair: Keypair, bankrun: ProgramTestContext +) -> None: + await bankrun_rpc( + program, + "test_owner", + [], + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "misc": program.program_id, + }, + ), + bankrun=bankrun, + ) + with raises(BanksClientError): + await bankrun_rpc( + program, + "test_owner", + [], + ctx=Context( + accounts={ + "data": bankrun.payer.pubkey(), + "misc": program.program_id, + }, + ), + bankrun=bankrun, + ) + + +@mark.asyncio +async def test_can_retrieve_events_when_simulating_transaction( + program: Program, bankrun: ProgramTestContext +) -> None: + resp = await bankrun_simulate( + program, "test_simulate", [44], ctx=EMPTY_CONTEXT, bankrun=bankrun + ) + expected_raw_first_entry = ( + "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh invoke [1]" + ) + events = resp.events + assert resp.raw[0] == expected_raw_first_entry + assert events[0].name == "E1" + assert events[0].data.data == 44 + assert events[1].name == "E2" + assert events[1].data.data == 1234 + assert events[2].name == "E3" + assert events[2].data.data == 9 + + +@mark.asyncio +async def test_can_use_i8_in_idl(program: Program, bankrun: ProgramTestContext) -> None: + data = Keypair() + await bankrun_rpc( + program, + "test_i8", + [-3], + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + pre_instructions=[ + await bankrun_create_instruction( + program.account["DataI8"], data, program.program_id, bankrun + ) + ], + signers=[data], + ), + bankrun=bankrun, + ) + data_account = await bankrun_fetch( + program.account["DataI8"], data.pubkey(), bankrun + ) + assert data_account.data == -3 + + +@async_fixture(scope="module") +async def data_i16_keypair(program: Program, bankrun: ProgramTestContext) -> Keypair: + data = Keypair() + await bankrun_rpc( + program, + "test_i16", + [-2048], + ctx=Context( + accounts={"data": data.pubkey(), "rent": RENT}, + pre_instructions=[ + await bankrun_create_instruction( + program.account["DataI16"], data, program.program_id, bankrun + ) + ], + signers=[data], + ), + bankrun=bankrun, + ) + return data + + +@mark.asyncio +async def test_can_use_i16_in_idl( + program: Program, data_i16_keypair: Keypair, bankrun: ProgramTestContext +) -> None: + data_account = await bankrun_fetch( + program.account["DataI16"], data_i16_keypair.pubkey(), bankrun + ) + assert data_account.data == -2048 + + +@mark.asyncio +async def test_fail_to_close_account_when_sending_lamports_to_itself( + program: Program, + initialized_keypair: Keypair, + bankrun: ProgramTestContext, +) -> None: + with raises(BanksClientError) as excinfo: + await bankrun_rpc( + program, + "test_close", + [], + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "sol_dest": initialized_keypair.pubkey(), + }, + ), + bankrun=bankrun, + ) + assert "0x7db" in excinfo.value.args[0] + + +@mark.asyncio +async def test_can_close_account( + program: Program, + initialized_keypair: Keypair, + bankrun: ProgramTestContext, +) -> None: + client = bankrun.banks_client + open_account = await client.get_account(initialized_keypair.pubkey()) + assert open_account is not None + before_balance_res = await client.get_account( + bankrun.payer.pubkey(), + ) + assert before_balance_res is not None + before_balance = before_balance_res.lamports + await bankrun_rpc( + program, + "test_close", + [], + ctx=Context( + accounts={ + "data": initialized_keypair.pubkey(), + "sol_dest": bankrun.payer.pubkey(), + }, + ), + bankrun=bankrun, + ) + after_balance_res = await client.get_account( + bankrun.payer.pubkey(), + ) + assert after_balance_res is not None + after_balance = after_balance_res.lamports + assert after_balance > before_balance + closed_account = await client.get_account( + initialized_keypair.pubkey(), + ) + assert closed_account is None + + +@mark.asyncio +async def test_can_use_instruction_data_in_accounts_constraints( + program: Program, bankrun: ProgramTestContext +) -> None: + seed = b"my-seed" + my_pda, nonce = Pubkey.find_program_address([seed, bytes(RENT)], program.program_id) + await bankrun_rpc( + program, + "test_instruction_constraint", + [nonce], + ctx=Context(accounts={"my_pda": my_pda, "my_account": RENT}), + bankrun=bankrun, + ) + + +@mark.asyncio +async def test_can_create_a_pda_with_instruction_data( + program: Program, bankrun: ProgramTestContext +) -> None: + seed = bytes([1, 2, 3, 4]) + domain = "my-domain" + foo = RENT + my_pda, nonce = Pubkey.find_program_address( + [b"my-seed", domain.encode(), bytes(foo), seed], program.program_id + ) + await bankrun_rpc( + program, + "test_pda_init", + [domain, seed, nonce], + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": bankrun.payer.pubkey(), + "foo": foo, + "rent": RENT, + "system_program": SYS_PROGRAM_ID, + } + ), + bankrun=bankrun, + ) + my_pda_account = await bankrun_fetch(program.account["DataU16"], my_pda, bankrun) + assert my_pda_account.data == 6 + + +@mark.asyncio +async def test_can_create_a_zero_copy_pda( + program: Program, bankrun: ProgramTestContext +) -> None: + my_pda, nonce = Pubkey.find_program_address([b"my-seed"], program.program_id) + await bankrun_rpc( + program, + "test_pda_init_zero_copy", + [], + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": bankrun.payer.pubkey(), + "rent": RENT, + "system_program": SYS_PROGRAM_ID, + }, + ), + bankrun=bankrun, + ) + my_pda_account = await bankrun_fetch( + program.account["DataZeroCopy"], my_pda, bankrun + ) + assert my_pda_account.data == 9 + assert my_pda_account.bump == nonce + + +@mark.asyncio +async def test_can_write_to_a_zero_copy_pda( + program: Program, bankrun: ProgramTestContext +) -> None: + my_pda, bump = Pubkey.find_program_address([b"my-seed"], program.program_id) + await bankrun_rpc( + program, + "test_pda_mut_zero_copy", + [], + ctx=Context( + accounts={ + "my_pda": my_pda, + "my_payer": bankrun.payer.pubkey(), + }, + ), + bankrun=bankrun, + ) + my_pda_account = await bankrun_fetch( + program.account["DataZeroCopy"], my_pda, bankrun + ) + assert my_pda_account.data == 1234 + assert my_pda_account.bump == bump + + +@mark.asyncio +async def test_can_create_a_token_account_from_seeds_pda( + program: Program, bankrun: ProgramTestContext +) -> None: + mint, mint_bump = Pubkey.find_program_address([b"my-mint-seed"], program.program_id) + my_pda, token_bump = Pubkey.find_program_address( + [b"my-token-seed"], program.program_id + ) + await bankrun_rpc( + program, + "test_token_seeds_init", + [], + ctx=Context( + accounts={ + "my_pda": my_pda, + "mint": mint, + "authority": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "rent": RENT, + "token_program": TOKEN_PROGRAM_ID, + }, + ), + bankrun=bankrun, + ) + mint_account = AsyncToken( + program.provider.connection, mint, TOKEN_PROGRAM_ID, bankrun.payer + ) + account_raw = await bankrun.banks_client.get_account(my_pda) + as_acc_info = as_account_info_resp(account_raw) + account = mint_account._create_account_info(as_acc_info) + assert account.is_frozen is False + assert account.is_initialized is True + assert account.amount == 0 + assert account.owner == bankrun.payer.pubkey() + assert account.mint == mint + + +@mark.asyncio +async def test_can_init_random_account( + program: Program, bankrun: ProgramTestContext +) -> None: + data = Keypair() + await bankrun_rpc( + program, + "test_init", + [], + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + ), + bankrun=bankrun, + ) + account = await bankrun_fetch(program.account["DataI8"], data.pubkey(), bankrun) + assert account.data == 3 + + +@mark.asyncio +async def test_can_init_random_account_prefunded( + program: Program, bankrun: ProgramTestContext +) -> None: + data = Keypair() + await bankrun_rpc( + program, + "test_init", + [], + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=bankrun.payer.pubkey(), + to_pubkey=data.pubkey(), + lamports=4039280, + ), + ), + ], + ), + bankrun=bankrun, + ) + account = await bankrun_fetch(program.account["DataI8"], data.pubkey(), bankrun) + assert account.data == 3 + + +@mark.asyncio +async def test_can_init_random_zero_copy_account( + program: Program, bankrun: ProgramTestContext +) -> None: + data = Keypair() + await bankrun_rpc( + program, + "test_init_zero_copy", + [], + ctx=Context( + accounts={ + "data": data.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[data], + ), + bankrun=bankrun, + ) + account = await bankrun_fetch( + program.account["DataZeroCopy"], data.pubkey(), bankrun + ) + assert account.data == 10 + assert account.bump == 2 + + +@mark.asyncio +async def test_can_create_random_mint_account( + program: Program, bankrun: ProgramTestContext +) -> None: + mint = Keypair() + await bankrun_rpc( + program, + "test_init_mint", + [], + ctx=Context( + accounts={ + "mint": mint.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[mint], + ), + bankrun=bankrun, + ) + client = AsyncToken( + program.provider.connection, + mint.pubkey(), + TOKEN_PROGRAM_ID, + bankrun.payer, + ) + mint_acc_raw = await bankrun.banks_client.get_account(client.pubkey) + mint_account = client._create_mint_info(as_account_info_resp(mint_acc_raw)) + assert mint_account.decimals == 6 + assert mint_account.mint_authority == bankrun.payer.pubkey() + assert mint_account.freeze_authority == bankrun.payer.pubkey() + + +@async_fixture(scope="module") +async def prefunded_mint(program: Program, bankrun: ProgramTestContext) -> Keypair: + mint = Keypair() + await bankrun_rpc( + program, + "test_init_mint", + [], + ctx=Context( + accounts={ + "mint": mint.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[mint], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=bankrun.payer.pubkey(), + to_pubkey=mint.pubkey(), + lamports=4039280, + ), + ), + ], + ), + bankrun=bankrun, + ) + return mint + + +@mark.asyncio +async def test_can_create_random_mint_account_prefunded( + program: Program, + prefunded_mint: Keypair, + bankrun: ProgramTestContext, +) -> None: + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + bankrun.payer, + ) + mint_acc_raw = await bankrun.banks_client.get_account(client.pubkey) + mint_account = client._create_mint_info(as_account_info_resp(mint_acc_raw)) + assert mint_account.decimals == 6 + assert mint_account.mint_authority == bankrun.payer.pubkey() + + +@mark.asyncio +async def test_can_create_random_token_account( + program: Program, + prefunded_mint: Keypair, + bankrun: ProgramTestContext, +) -> None: + token = Keypair() + await bankrun_rpc( + program, + "test_init_token", + [], + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + ), + bankrun=bankrun, + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + bankrun.payer, + ) + account_raw = await bankrun.banks_client.get_account(token.pubkey()) + account = client._create_account_info(as_account_info_resp(account_raw)) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == bankrun.payer.pubkey() + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_can_create_random_token_account_with_prefunding( + program: Program, + prefunded_mint: Keypair, + bankrun: ProgramTestContext, +) -> None: + token = Keypair() + await bankrun_rpc( + program, + "test_init_token", + [], + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=bankrun.payer.pubkey(), + to_pubkey=token.pubkey(), + lamports=4039280, + ), + ) + ], + ), + bankrun=bankrun, + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + bankrun.payer, + ) + account_raw = await bankrun.banks_client.get_account(token.pubkey()) + account = client._create_account_info(as_account_info_resp(account_raw)) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == bankrun.payer.pubkey() + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_can_create_random_token_account_with_prefunding_under_rent_exemption( + program: Program, + prefunded_mint: Keypair, + bankrun: ProgramTestContext, +) -> None: + token = Keypair() + await bankrun_rpc( + program, + "test_init_token", + [], + ctx=Context( + accounts={ + "token": token.pubkey(), + "mint": prefunded_mint.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + "token_program": TOKEN_PROGRAM_ID, + "rent": RENT, + }, + signers=[token], + pre_instructions=[ + transfer( + TransferParams( + from_pubkey=bankrun.payer.pubkey(), + to_pubkey=token.pubkey(), + lamports=1, + ), + ) + ], + ), + bankrun=bankrun, + ) + client = AsyncToken( + program.provider.connection, + prefunded_mint.pubkey(), + TOKEN_PROGRAM_ID, + bankrun.payer, + ) + account_raw = await bankrun.banks_client.get_account(token.pubkey()) + account = client._create_account_info(as_account_info_resp(account_raw)) + assert not account.is_frozen + assert account.amount == 0 + assert account.is_initialized + assert account.owner == bankrun.payer.pubkey() + assert account.mint == prefunded_mint.pubkey() + + +@mark.asyncio +async def test_init_multiple_accounts_via_composite_payer( + program: Program, bankrun: ProgramTestContext +) -> None: + data1 = Keypair() + data2 = Keypair() + await bankrun_rpc( + program, + "test_composite_payer", + [], + ctx=Context( + accounts={ + "composite": { + "data": data1.pubkey(), + "payer": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + "data": data2.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + signers=[data1, data2], + ), + bankrun=bankrun, + ) + account1 = await bankrun_fetch(program.account["DataI8"], data1.pubkey(), bankrun) + assert account1.data == 1 + + account2 = await bankrun_fetch(program.account["Data"], data2.pubkey(), bankrun) + assert account2.udata == 2 + assert account2.idata == 3 + + +@mark.asyncio +async def test_can_create_associated_token_account( + program: Program, bankrun: ProgramTestContext +) -> None: + # TODO + pass + + +@mark.asyncio +async def test_can_validate_associated_token_constraints( + program: Program, bankrun: ProgramTestContext +) -> None: + # TODO + pass + + +@mark.asyncio +async def test_can_use_pdas_with_empty_seeds( + program: Program, bankrun: ProgramTestContext +) -> None: + pda, bump = Pubkey.find_program_address([], program.program_id) + await bankrun_rpc( + program, + "test_init_with_empty_seeds", + [], + ctx=Context( + accounts={ + "pda": pda, + "authority": bankrun.payer.pubkey(), + "system_program": SYS_PROGRAM_ID, + }, + ), + bankrun=bankrun, + ) + await bankrun_rpc( + program, + "test_empty_seeds_constraint", + [], + ctx=Context( + accounts={ + "pda": pda, + }, + ), + bankrun=bankrun, + ) + pda2, _ = Pubkey.find_program_address( + [b"non-empty"], + program.program_id, + ) + with raises(BanksClientError) as excinfo: + await bankrun_rpc( + program, + "test_empty_seeds_constraint", + [], + ctx=Context( + accounts={ + "pda": pda2, + }, + ), + bankrun=bankrun, + ) + assert "0x7d6" in excinfo.value.args[0] + + +@async_fixture(scope="module") +async def if_needed_acc(program: Program, bankrun: ProgramTestContext) -> Keypair: + keypair = Keypair() + await bankrun_rpc( + program, + "test_init_if_needed", + [1], + ctx=Context( + accounts={ + "data": keypair.pubkey(), + "system_program": SYS_PROGRAM_ID, + "payer": bankrun.payer.pubkey(), + }, + signers=[keypair], + ), + bankrun=bankrun, + ) + return keypair + + +@mark.asyncio +async def test_can_init_if_needed_a_new_account( + program: Program, + if_needed_acc: Keypair, + bankrun: ProgramTestContext, +) -> None: + account = await bankrun_fetch( + program.account["DataU16"], if_needed_acc.pubkey(), bankrun + ) + assert account.data == 1 + + +@mark.asyncio +async def test_can_init_if_needed_a_previously_created_account( + program: Program, + if_needed_acc: Keypair, + bankrun: ProgramTestContext, +) -> None: + await bankrun_rpc( + program, + "test_init_if_needed", + [3], + ctx=Context( + accounts={ + "data": if_needed_acc.pubkey(), + "system_program": SYS_PROGRAM_ID, + "payer": bankrun.payer.pubkey(), + }, + signers=[if_needed_acc], + ), + bankrun=bankrun, + ) + account = await bankrun_fetch( + program.account["DataU16"], if_needed_acc.pubkey(), bankrun + ) + assert account.data == 3 diff --git a/basics/anchorpy-main/tests/test_multisig.py b/basics/anchorpy-main/tests/test_multisig.py new file mode 100644 index 0000000..696c417 --- /dev/null +++ b/basics/anchorpy-main/tests/test_multisig.py @@ -0,0 +1,192 @@ +"""Mimics anchor/tests/multisig.""" +from anchorpy import Context, Program, Provider +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solders.instruction import AccountMeta +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.sysvar import RENT + +CreatedMultisig = tuple[Keypair, int, list[Pubkey], int, Pubkey, Keypair, Keypair] +CreatedTransaction = tuple[Keypair, list[dict], bytes, Keypair, Pubkey, list[Pubkey]] + +workspace = workspace_fixture( + "anchor/tests/multisig/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["multisig"] + + +@fixture(scope="module") +def provider(program: Program) -> Provider: + """Get a Provider instance.""" + return program.provider + + +@async_fixture(scope="module") +async def created_multisig(program: Program) -> CreatedMultisig: + """Run the create_multisig RPC function.""" + multisig = Keypair() + multisig_signer, nonce = Pubkey.find_program_address( + [bytes(multisig.pubkey())], program.program_id + ) + multisig_size = 200 + owner_a, owner_b, owner_c = Keypair(), Keypair(), Keypair() + owners = [owner_a.pubkey(), owner_b.pubkey(), owner_c.pubkey()] + threshold = 2 + await program.rpc["create_multisig"]( + owners, + threshold, + nonce, + ctx=Context( + accounts={ + "multisig": multisig.pubkey(), + "rent": RENT, + }, + pre_instructions=[ + await program.account["Multisig"].create_instruction( + multisig, multisig_size + ), + ], + signers=[multisig], + ), + ) + return multisig, nonce, owners, threshold, multisig_signer, owner_a, owner_b + + +@mark.asyncio +async def test_created_multisig( + created_multisig: CreatedMultisig, + program: Program, +) -> None: + multisig, nonce, owners, threshold = created_multisig[:4] + multisig_account = await program.account["Multisig"].fetch(multisig.pubkey()) + assert multisig_account.nonce == nonce + assert multisig_account.threshold == threshold + assert multisig_account.owners == owners + + +@async_fixture(scope="module") +async def created_transaction( + program: Program, + created_multisig: CreatedMultisig, +) -> CreatedTransaction: + owner_d = Keypair() + multisig, _, owners, _, multisig_signer, owner_a, _ = created_multisig + accounts = [ + program.type["TransactionAccount"]( + pubkey=multisig.pubkey(), + is_writable=True, + is_signer=False, + ), + program.type["TransactionAccount"]( + pubkey=multisig_signer, + is_writable=False, + is_signer=True, + ), + ] + new_owners = [*owners[:2], owner_d.pubkey()] + data = program.coder.instruction.encode("set_owners", {"owners": new_owners}) + transaction = Keypair() + tx_size = 1000 + await program.rpc["create_transaction"]( + program.program_id, + accounts, + data, + ctx=Context( + accounts={ + "multisig": multisig.pubkey(), + "transaction": transaction.pubkey(), + "proposer": owner_a.pubkey(), + "rent": RENT, + }, + pre_instructions=[ + await program.account["Transaction"].create_instruction( + transaction, + tx_size, + ), + ], + signers=[transaction, owner_a], + ), + ) + return transaction, accounts, data, multisig, multisig_signer, new_owners + + +@mark.asyncio +async def test_created_transaction( + created_transaction: CreatedTransaction, + program: Program, +) -> None: + transaction, accounts, data, multisig, _, _ = created_transaction + tx_account = await program.account["Transaction"].fetch(transaction.pubkey()) + assert tx_account.program_id == program.program_id + assert tx_account.accounts == accounts + assert tx_account.data == data + assert tx_account.multisig == multisig.pubkey() + assert tx_account.did_execute is False + + +@async_fixture(scope="module") +async def executed_transaction( + program: Program, + created_transaction: CreatedTransaction, + created_multisig: CreatedMultisig, +) -> None: + transaction, _, _, multisig, multisig_signer, _ = created_transaction + owner_b = created_multisig[6] + await program.rpc["approve"]( + ctx=Context( + accounts={ + "multisig": multisig.pubkey(), + "transaction": transaction.pubkey(), + "owner": owner_b.pubkey(), + }, + signers=[owner_b], + ), + ) + remaining_accounts_raw = program.instruction["set_owners"].accounts( + {"multisig": multisig.pubkey(), "multisig_signer": multisig_signer} + ) + with_corrected_signer = [] + for meta in remaining_accounts_raw: + to_append = ( + AccountMeta( + pubkey=meta.pubkey, is_signer=False, is_writable=meta.is_writable + ) + if meta.pubkey == multisig_signer + else meta + ) + with_corrected_signer.append(to_append) + remaining_accounts = with_corrected_signer + [ + AccountMeta(pubkey=program.program_id, is_signer=False, is_writable=False) + ] + ctx = Context( + accounts={ + "multisig": multisig.pubkey(), + "multisig_signer": multisig_signer, + "transaction": transaction.pubkey(), + }, + remaining_accounts=remaining_accounts, + ) + await program.rpc["execute_transaction"](ctx=ctx) + + +@mark.asyncio +async def test_executed_transaction( + created_multisig: CreatedMultisig, + created_transaction: CreatedTransaction, + executed_transaction: None, + program: Program, +) -> None: + multisig, nonce, _, threshold = created_multisig[:4] + new_owners = created_transaction[5] + multisig_account = await program.account["Multisig"].fetch(multisig.pubkey()) + assert multisig_account.nonce == nonce + assert multisig_account.threshold == threshold + assert multisig_account.owners == new_owners diff --git a/basics/anchorpy-main/tests/test_pyth.py b/basics/anchorpy-main/tests/test_pyth.py new file mode 100644 index 0000000..ad5ef28 --- /dev/null +++ b/basics/anchorpy-main/tests/test_pyth.py @@ -0,0 +1,135 @@ +"""Mimics anchor/tests/misc/tests/misc.js.""" +from dataclasses import dataclass + +from anchorpy import Context, Program +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from construct import Int32sl, Int64ul +from pytest import fixture, mark +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.system_program import ( + CreateAccountParams, + create_account, +) + +workspace = workspace_fixture( + "anchor/tests/pyth/", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["pyth"] + + +async def create_price_feed( + oracle_program: Program, + init_price: int, + expo: int, +) -> Pubkey: + conf = int((init_price / 10) * 10**-expo) + space = 3312 + mbre_resp = ( + await oracle_program.provider.connection.get_minimum_balance_for_rent_exemption( + space, + ) + ) + collateral_token_feed = Keypair() + await oracle_program.rpc["initialize"]( + int(init_price * 10**-expo), + expo, + conf, + ctx=Context( + accounts={"price": collateral_token_feed.pubkey()}, + signers=[collateral_token_feed], + pre_instructions=[ + create_account( + CreateAccountParams( + from_pubkey=oracle_program.provider.wallet.public_key, + to_pubkey=collateral_token_feed.pubkey(), + space=3312, + lamports=mbre_resp.value, + owner=oracle_program.program_id, + ), + ), + ], + ), + ) + return collateral_token_feed.pubkey() + + +async def set_feed_price( + oracle_program: Program, + new_price: int, + price_feed: Pubkey, +) -> None: + data = await get_feed_data(oracle_program, price_feed) + await oracle_program.rpc["set_price"]( + int(new_price * 10**-data.exponent), + ctx=Context(accounts={"price": price_feed}), + ) + + +@dataclass +class PriceData: + exponent: int + price: int + + +def parse_price_data(data: bytes) -> PriceData: + exponent = Int32sl.parse(data[20:24]) + raw_price = Int64ul.parse(data[208:216]) + price = raw_price * 10**exponent + return PriceData(exponent, price) + + +async def get_feed_data( + oracle_program: Program, + price_feed: Pubkey, +) -> PriceData: + info = await oracle_program.provider.connection.get_account_info( + price_feed, + encoding="base64", + ) + val = info.value + if val is None: + raise ValueError("Account does not exist.") + return parse_price_data(val.data) + + +@mark.asyncio +async def test_initialize(program: Program) -> None: + price = 50000 + price_feed_address = await create_price_feed( + oracle_program=program, + init_price=price, + expo=-6, + ) + feed_data = await get_feed_data(program, price_feed_address) + assert feed_data.price == price + + +@mark.asyncio +async def test_change_feed_price(program: Program) -> None: + price = 50000 + expo = -7 + price_feed_address = await create_price_feed( + oracle_program=program, + init_price=price, + expo=expo, + ) + feed_data_before = await get_feed_data(program, price_feed_address) + assert feed_data_before.price == price + assert feed_data_before.exponent == expo + new_price = 55000 + await set_feed_price(program, new_price, price_feed_address) + feed_data_after = await get_feed_data(program, price_feed_address) + assert feed_data_after.price == new_price + assert feed_data_after.exponent == expo + + +@mark.asyncio +async def test_enum(program: Program) -> None: + corp_act = program.type["CorpAction"] + assert corp_act.NoCorpAct().index == 0 diff --git a/basics/anchorpy-main/tests/test_zero_copy.py b/basics/anchorpy-main/tests/test_zero_copy.py new file mode 100644 index 0000000..4ef96ee --- /dev/null +++ b/basics/anchorpy-main/tests/test_zero_copy.py @@ -0,0 +1,334 @@ +"""Mimics anchor/tests/zero-copy.""" +from pathlib import Path + +from anchorpy import ( + Context, + Program, + Provider, +) +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType +from pytest import fixture, mark, raises +from pytest_asyncio import fixture as async_fixture +from solana.rpc.core import RPCException +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.sysvar import RENT + +PATH = Path("anchor/tests/zero-copy") +DEFAULT_PUBKEY = Pubkey.from_string("11111111111111111111111111111111") + +workspace = workspace_fixture( + "anchor/tests/zero-copy", build_cmd="anchor build --skip-lint" +) + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + return workspace["zero_copy"] + + +@fixture(scope="module") +def program_cpi(workspace: dict[str, Program]) -> Program: + return workspace["zero_cpi"] + + +@async_fixture(scope="module") +async def provider(program: Program) -> Provider: + """Get a Provider instance.""" + print(program.idl) + return program.provider + + +@async_fixture(scope="module") +async def foo(program: Program) -> Keypair: + foo_keypair = Keypair() + await program.rpc["create_foo"]( + ctx=Context( + accounts={ + "foo": foo_keypair.pubkey(), + "authority": program.provider.wallet.public_key, + "rent": RENT, + }, + pre_instructions=[ + await program.account["Foo"].create_instruction(foo_keypair) + ], + signers=[foo_keypair], + ) + ) + return foo_keypair + + +@mark.asyncio +async def test_create_foo(foo: Keypair, provider: Provider, program: Program) -> None: + account = await program.account["Foo"].fetch(foo.pubkey()) + assert account.authority == provider.wallet.public_key + assert account.data == 0 + assert account.second_data == 0 + assert account.second_authority == list(bytes(provider.wallet.public_key)) + + +@async_fixture(scope="module") +async def update_foo(program: Program, foo: Keypair) -> None: + await program.rpc["update_foo"]( + 1234, + ctx=Context( + accounts={ + "foo": foo.pubkey(), + "authority": program.provider.wallet.public_key, + }, + ), + ) + + +@mark.asyncio +async def test_update_foo( + foo: Keypair, provider: Provider, program: Program, update_foo: None +) -> None: + account = await program.account["Foo"].fetch(foo.pubkey()) + assert account.authority == provider.wallet.public_key + assert account.data == 1234 + assert account.second_data == 0 + assert account.second_authority == list(bytes(program.provider.wallet.public_key)) + + +@async_fixture(scope="module") +async def update_foo_second(program: Program, foo: Keypair, update_foo: None) -> None: + await program.rpc["update_foo_second"]( + 55, + ctx=Context( + accounts={ + "foo": foo.pubkey(), + "second_authority": program.provider.wallet.public_key, + }, + ), + ) + + +@mark.asyncio +async def test_update_foo_second( + foo: Keypair, provider: Provider, program: Program, update_foo_second: None +) -> None: + account = await program.account["Foo"].fetch(foo.pubkey()) + assert account.authority == provider.wallet.public_key + assert account.data == 1234 + assert account.second_data == 55 + assert account.second_authority == list(bytes(program.provider.wallet.public_key)) + + +@async_fixture(scope="module") +async def bar( + program: Program, provider: Provider, foo: Keypair, update_foo_second: None +) -> Pubkey: + bar_pubkey = Pubkey.find_program_address( + [bytes(provider.wallet.public_key), bytes(foo.pubkey())], program.program_id + )[0] + await program.rpc["create_bar"]( + ctx=Context( + accounts={ + "bar": bar_pubkey, + "authority": provider.wallet.public_key, + "foo": foo.pubkey(), + "system_program": SYS_PROGRAM_ID, + } + ) + ) + return bar_pubkey + + +@mark.asyncio +async def test_create_bar(provider: Provider, program: Program, bar: Pubkey) -> None: + account = await program.account["Bar"].fetch(bar) + assert account.authority == provider.wallet.public_key + assert account.data == 0 + + +@async_fixture(scope="module") +async def update_associated_zero_copy_account( + program: Program, + provider: Provider, + foo: Keypair, + bar: Pubkey, +) -> None: + await program.rpc["update_bar"]( + 99, + ctx=Context( + accounts={ + "bar": bar, + "authority": program.provider.wallet.public_key, + "foo": foo.pubkey(), + }, + ), + ) + + +@mark.asyncio +async def test_update_associated_zero_copy_account( + provider: Provider, + program: Program, + bar: Pubkey, + update_associated_zero_copy_account: None, +) -> None: + account = await program.account["Bar"].fetch(bar) + assert account.authority == provider.wallet.public_key + assert account.data == 99 + + +@async_fixture(scope="module") +async def check_cpi( + program_cpi: Program, + program: Program, + provider: Provider, + foo: Keypair, + bar: Pubkey, + update_associated_zero_copy_account: None, +) -> None: + await program_cpi.rpc["check_cpi"]( + 1337, + ctx=Context( + accounts={ + "bar": bar, + "authority": provider.wallet.public_key, + "foo": foo.pubkey(), + "zero_copy_program": program.program_id, + }, + ), + ) + + +@mark.asyncio +async def test_check_cpi( + provider: Provider, + program: Program, + bar: Pubkey, + check_cpi: None, +) -> None: + account = await program.account["Bar"].fetch(bar) + assert account.authority == provider.wallet.public_key + assert account.data == 1337 + + +@async_fixture(scope="module") +async def event_q( + program: Program, + check_cpi: None, +) -> Keypair: + event_q_keypair = Keypair() + size = 1000000 + 8 + await program.rpc["create_large_account"]( + ctx=Context( + accounts={ + "event_q": event_q_keypair.pubkey(), + "rent": RENT, + }, + pre_instructions=[ + await program.account["EventQ"].create_instruction( + event_q_keypair, size + ) + ], + signers=[event_q_keypair], + ), + ) + return event_q_keypair + + +@mark.asyncio +async def test_event_q( + program: Program, + event_q: Keypair, +) -> None: + account = await program.account["EventQ"].fetch(event_q.pubkey()) + events = account.events + assert len(events) == 25000 + for event in events: + assert event.from_ == DEFAULT_PUBKEY + assert event.data == 0 + + +@mark.asyncio +async def test_update_event_q( + program: Program, + provider: Provider, + event_q: Keypair, +) -> None: + await program.rpc["update_large_account"]( + 0, + 48, + ctx=Context( + accounts={ + "event_q": event_q.pubkey(), + "from": provider.wallet.public_key, + }, + ), + ) + account = await program.account["EventQ"].fetch(event_q.pubkey()) + events = account.events + assert len(events) == 25000 + for idx, event in enumerate(events): + if idx == 0: + assert event.from_ == provider.wallet.public_key + assert event.data == 48 + else: + assert event.from_ == DEFAULT_PUBKEY + assert event.data == 0 + await program.rpc["update_large_account"]( + 11111, + 1234, + ctx=Context( + accounts={ + "event_q": event_q.pubkey(), + "from": provider.wallet.public_key, + }, + ), + ) + account = await program.account["EventQ"].fetch(event_q.pubkey()) + events = account.events + assert len(events) == 25000 + for idx, event in enumerate(events): + if idx == 0: + assert event.from_ == provider.wallet.public_key + assert event.data == 48 + elif idx == 11111: + assert event.from_ == provider.wallet.public_key + assert event.data == 1234 + else: + assert event.from_ == DEFAULT_PUBKEY + assert event.data == 0 + await program.rpc["update_large_account"]( + 24999, + 99, + ctx=Context( + accounts={ + "event_q": event_q.pubkey(), + "from": provider.wallet.public_key, + }, + ), + ) + account = await program.account["EventQ"].fetch(event_q.pubkey()) + events = account.events + assert len(events) == 25000 + for idx, event in enumerate(events): + if idx == 0: + assert event.from_ == provider.wallet.public_key + assert event.data == 48 + elif idx == 11111: + assert event.from_ == provider.wallet.public_key + assert event.data == 1234 + elif idx == 24999: + assert event.from_ == provider.wallet.public_key + assert event.data == 99 + else: + assert event.from_ == DEFAULT_PUBKEY + assert event.data == 0 + with raises(RPCException): + await program.rpc["update_large_account"]( + 25000, + 99, + ctx=Context( + accounts={ + "event_q": event_q.pubkey(), + "from": provider.wallet.public_key, + }, + ), + ) From 8b8e873ccd082dbdd8ca86aa80b7f0ce71fc1ff4 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:30:08 +0530 Subject: [PATCH 09/19] Delete basics/anchorpy-main/tests/bh --- basics/anchorpy-main/tests/bh | 1 - 1 file changed, 1 deletion(-) delete mode 100644 basics/anchorpy-main/tests/bh diff --git a/basics/anchorpy-main/tests/bh b/basics/anchorpy-main/tests/bh deleted file mode 100644 index 8b13789..0000000 --- a/basics/anchorpy-main/tests/bh +++ /dev/null @@ -1 +0,0 @@ - From 5b0a63a2af2e75cce35e9d00f3969ff9800983a0 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:30:44 +0530 Subject: [PATCH 10/19] Create as --- basics/anchorpy-main/tests/idls/as | 1 + 1 file changed, 1 insertion(+) create mode 100644 basics/anchorpy-main/tests/idls/as diff --git a/basics/anchorpy-main/tests/idls/as b/basics/anchorpy-main/tests/idls/as new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/as @@ -0,0 +1 @@ + From ecb251e6bd65fe2a91e218e8c291e36321bf0874 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:31:35 +0530 Subject: [PATCH 11/19] Add files via upload --- basics/anchorpy-main/tests/idls/basic_0.json | 14 + basics/anchorpy-main/tests/idls/basic_1.json | 57 + basics/anchorpy-main/tests/idls/basic_2.json | 64 + basics/anchorpy-main/tests/idls/basic_5.json | 80 + .../tests/idls/candy_machine.json | 915 ++++ .../tests/idls/cashiers_check.json | 191 + basics/anchorpy-main/tests/idls/chat.json | 157 + .../tests/idls/clientgen_example_program.json | 737 +++ .../anchorpy-main/tests/idls/composite.json | 86 + .../tests/idls/counter_auth.json | 5 + basics/anchorpy-main/tests/idls/errors.json | 88 + basics/anchorpy-main/tests/idls/escrow.json | 161 + basics/anchorpy-main/tests/idls/events.json | 28 + basics/anchorpy-main/tests/idls/ido_pool.json | 397 ++ basics/anchorpy-main/tests/idls/jet.json | 1905 +++++++ basics/anchorpy-main/tests/idls/jet_auth.json | 99 + .../anchorpy-main/tests/idls/jet_rewards.json | 1177 +++++ .../tests/idls/merkle_distributor.json | 480 ++ basics/anchorpy-main/tests/idls/multisig.json | 277 + basics/anchorpy-main/tests/idls/puppet.json | 55 + .../tests/idls/puppet_master.json | 30 + basics/anchorpy-main/tests/idls/pyth.json | 99 + .../anchorpy-main/tests/idls/quarry_mine.json | 980 ++++ .../tests/idls/sol_cerberus.json | 1464 ++++++ .../anchorpy-main/tests/idls/spl_token.json | 844 +++ basics/anchorpy-main/tests/idls/swap.json | 352 ++ .../anchorpy-main/tests/idls/switchboard.json | 1 + .../idls/switchboard_v2.mainnet.06022022.json | 4601 +++++++++++++++++ basics/anchorpy-main/tests/idls/sysvars.json | 27 + .../anchorpy-main/tests/idls/tictactoe.json | 179 + .../anchorpy-main/tests/idls/typescript.json | 11 + 31 files changed, 15561 insertions(+) create mode 100644 basics/anchorpy-main/tests/idls/basic_0.json create mode 100644 basics/anchorpy-main/tests/idls/basic_1.json create mode 100644 basics/anchorpy-main/tests/idls/basic_2.json create mode 100644 basics/anchorpy-main/tests/idls/basic_5.json create mode 100644 basics/anchorpy-main/tests/idls/candy_machine.json create mode 100644 basics/anchorpy-main/tests/idls/cashiers_check.json create mode 100644 basics/anchorpy-main/tests/idls/chat.json create mode 100644 basics/anchorpy-main/tests/idls/clientgen_example_program.json create mode 100644 basics/anchorpy-main/tests/idls/composite.json create mode 100644 basics/anchorpy-main/tests/idls/counter_auth.json create mode 100644 basics/anchorpy-main/tests/idls/errors.json create mode 100644 basics/anchorpy-main/tests/idls/escrow.json create mode 100644 basics/anchorpy-main/tests/idls/events.json create mode 100644 basics/anchorpy-main/tests/idls/ido_pool.json create mode 100644 basics/anchorpy-main/tests/idls/jet.json create mode 100644 basics/anchorpy-main/tests/idls/jet_auth.json create mode 100644 basics/anchorpy-main/tests/idls/jet_rewards.json create mode 100644 basics/anchorpy-main/tests/idls/merkle_distributor.json create mode 100644 basics/anchorpy-main/tests/idls/multisig.json create mode 100644 basics/anchorpy-main/tests/idls/puppet.json create mode 100644 basics/anchorpy-main/tests/idls/puppet_master.json create mode 100644 basics/anchorpy-main/tests/idls/pyth.json create mode 100644 basics/anchorpy-main/tests/idls/quarry_mine.json create mode 100644 basics/anchorpy-main/tests/idls/sol_cerberus.json create mode 100644 basics/anchorpy-main/tests/idls/spl_token.json create mode 100644 basics/anchorpy-main/tests/idls/swap.json create mode 100644 basics/anchorpy-main/tests/idls/switchboard.json create mode 100644 basics/anchorpy-main/tests/idls/switchboard_v2.mainnet.06022022.json create mode 100644 basics/anchorpy-main/tests/idls/sysvars.json create mode 100644 basics/anchorpy-main/tests/idls/tictactoe.json create mode 100644 basics/anchorpy-main/tests/idls/typescript.json diff --git a/basics/anchorpy-main/tests/idls/basic_0.json b/basics/anchorpy-main/tests/idls/basic_0.json new file mode 100644 index 0000000..6eb8507 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/basic_0.json @@ -0,0 +1,14 @@ +{ + "version": "0.0.0", + "name": "basic_0", + "instructions": [ + { + "name": "initialize", + "accounts": [], + "args": [] + } + ], + "metadata": { + "address": "8nA4T4UFdYz1sKuEFdnj2mYcJ6hp79Tw14sPvTQVWwVb" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/basic_1.json b/basics/anchorpy-main/tests/idls/basic_1.json new file mode 100644 index 0000000..810ed34 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/basic_1.json @@ -0,0 +1,57 @@ +{ + "version": "0.0.0", + "name": "basic_1", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "myAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": "u64" + } + ] + }, + { + "name": "update", + "accounts": [ + { + "name": "myAccount", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "MyAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": "u64" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/basic_2.json b/basics/anchorpy-main/tests/idls/basic_2.json new file mode 100644 index 0000000..7777c45 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/basic_2.json @@ -0,0 +1,64 @@ +{ + "version": "0.0.0", + "name": "basic_2", + "instructions": [ + { + "name": "create", + "accounts": [ + { + "name": "counter", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "authority", + "type": "publicKey" + } + ] + }, + { + "name": "increment", + "accounts": [ + { + "name": "counter", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Counter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "count", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "address": "4V6T2muqU1mHSg5XfK3SMgxZWQRSEUh72LzwGTLHGzsY" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/basic_5.json b/basics/anchorpy-main/tests/idls/basic_5.json new file mode 100644 index 0000000..ecde024 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/basic_5.json @@ -0,0 +1,80 @@ +{ + "version": "0.0.0", + "name": "basic_5", + "instructions": [ + { + "name": "createMint", + "accounts": [ + { + "name": "mint", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "createToken", + "accounts": [ + { + "name": "token", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": true, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Mint", + "type": { + "kind": "struct", + "fields": [ + { + "name": "supply", + "type": "u32" + } + ] + } + }, + { + "name": "Token", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u32" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + } + ] + } + } + ], + "metadata": { + "address": "99cGumFqPFhCLTUxs9BoyhXAV6c19Ca44BYZVn6KG1Pu" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/candy_machine.json b/basics/anchorpy-main/tests/idls/candy_machine.json new file mode 100644 index 0000000..dcae308 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/candy_machine.json @@ -0,0 +1,915 @@ +{ + "version": "4.0.2", + "name": "candy_machine", + "instructions": [ + { + "name": "initializeCandyMachine", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "wallet", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": { + "defined": "CandyMachineData" + } + } + ] + }, + { + "name": "updateCandyMachine", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "wallet", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": { + "defined": "CandyMachineData" + } + } + ] + }, + { + "name": "updateAuthority", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "wallet", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "newAuthority", + "type": { + "option": "publicKey" + } + } + ] + }, + { + "name": "addConfigLines", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "index", + "type": "u32" + }, + { + "name": "configLines", + "type": { + "vec": { + "defined": "ConfigLine" + } + } + } + ] + }, + { + "name": "setCollection", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "collectionPda", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "metadata", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "edition", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionAuthorityRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "removeCollection", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "collectionPda", + "isMut": true, + "isSigner": false + }, + { + "name": "metadata", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionAuthorityRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "mintNft", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "candyMachineCreator", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "wallet", + "isMut": true, + "isSigner": false + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "mintAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "updateAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "masterEdition", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false, + "docs": [ + "Account not actually used." + ] + }, + { + "name": "recentBlockhashes", + "isMut": false, + "isSigner": false + }, + { + "name": "instructionSysvarAccount", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "creatorBump", + "type": "u8" + } + ] + }, + { + "name": "setCollectionDuringMint", + "accounts": [ + { + "name": "candyMachine", + "isMut": false, + "isSigner": false + }, + { + "name": "metadata", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "collectionPda", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "instructions", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionMint", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionMetadata", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionMasterEdition", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "collectionAuthorityRecord", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "withdrawFunds", + "accounts": [ + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "CandyMachine", + "docs": [ + "Candy machine state and config data." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "wallet", + "type": "publicKey" + }, + { + "name": "tokenMint", + "type": { + "option": "publicKey" + } + }, + { + "name": "itemsRedeemed", + "type": "u64" + }, + { + "name": "data", + "type": { + "defined": "CandyMachineData" + } + } + ] + } + }, + { + "name": "CollectionPDA", + "docs": [ + "Collection PDA account" + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "candyMachine", + "type": "publicKey" + } + ] + } + } + ], + "types": [ + { + "name": "CandyMachineData", + "docs": [ + "Candy machine settings data." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "uuid", + "type": "string" + }, + { + "name": "price", + "type": "u64" + }, + { + "name": "symbol", + "docs": [ + "The symbol for the asset" + ], + "type": "string" + }, + { + "name": "sellerFeeBasisPoints", + "docs": [ + "Royalty basis points that goes to creators in secondary sales (0-10000)" + ], + "type": "u16" + }, + { + "name": "maxSupply", + "type": "u64" + }, + { + "name": "isMutable", + "type": "bool" + }, + { + "name": "retainAuthority", + "type": "bool" + }, + { + "name": "goLiveDate", + "type": { + "option": "i64" + } + }, + { + "name": "endSettings", + "type": { + "option": { + "defined": "EndSettings" + } + } + }, + { + "name": "creators", + "type": { + "vec": { + "defined": "Creator" + } + } + }, + { + "name": "hiddenSettings", + "type": { + "option": { + "defined": "HiddenSettings" + } + } + }, + { + "name": "whitelistMintSettings", + "type": { + "option": { + "defined": "WhitelistMintSettings" + } + } + }, + { + "name": "itemsAvailable", + "type": "u64" + }, + { + "name": "gatekeeper", + "docs": [ + "If [`Some`] requires gateway tokens on mint" + ], + "type": { + "option": { + "defined": "GatekeeperConfig" + } + } + } + ] + } + }, + { + "name": "ConfigLine", + "docs": [ + "Individual config line for storing NFT data pre-mint." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "uri", + "docs": [ + "URI pointing to JSON representing the asset" + ], + "type": "string" + } + ] + } + }, + { + "name": "EndSettings", + "type": { + "kind": "struct", + "fields": [ + { + "name": "endSettingType", + "type": { + "defined": "EndSettingType" + } + }, + { + "name": "number", + "type": "u64" + } + ] + } + }, + { + "name": "Creator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "verified", + "type": "bool" + }, + { + "name": "share", + "type": "u8" + } + ] + } + }, + { + "name": "HiddenSettings", + "docs": [ + "Hidden Settings for large mints used with offline data." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "hash", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "WhitelistMintSettings", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mode", + "type": { + "defined": "WhitelistMintMode" + } + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "presale", + "type": "bool" + }, + { + "name": "discountPrice", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "GatekeeperConfig", + "docs": [ + "Configurations options for the gatekeeper." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "gatekeeperNetwork", + "docs": [ + "The network for the gateway token required" + ], + "type": "publicKey" + }, + { + "name": "expireOnUse", + "docs": [ + "Whether or not the token should expire after minting.", + "The gatekeeper network must support this if true." + ], + "type": "bool" + } + ] + } + }, + { + "name": "EndSettingType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Date" + }, + { + "name": "Amount" + } + ] + } + }, + { + "name": "WhitelistMintMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "BurnEveryTime" + }, + { + "name": "NeverBurn" + } + ] + } + } + ], + "errors": [ + { + "code": 6000, + "name": "IncorrectOwner", + "msg": "Account does not have correct owner!" + }, + { + "code": 6001, + "name": "Uninitialized", + "msg": "Account is not initialized!" + }, + { + "code": 6002, + "name": "MintMismatch", + "msg": "Mint Mismatch!" + }, + { + "code": 6003, + "name": "IndexGreaterThanLength", + "msg": "Index greater than length!" + }, + { + "code": 6004, + "name": "NumericalOverflowError", + "msg": "Numerical overflow error!" + }, + { + "code": 6005, + "name": "TooManyCreators", + "msg": "Can only provide up to 4 creators to candy machine (because candy machine is one)!" + }, + { + "code": 6006, + "name": "UuidMustBeExactly6Length", + "msg": "Uuid must be exactly of 6 length" + }, + { + "code": 6007, + "name": "NotEnoughTokens", + "msg": "Not enough tokens to pay for this minting" + }, + { + "code": 6008, + "name": "NotEnoughSOL", + "msg": "Not enough SOL to pay for this minting" + }, + { + "code": 6009, + "name": "TokenTransferFailed", + "msg": "Token transfer failed" + }, + { + "code": 6010, + "name": "CandyMachineEmpty", + "msg": "Candy machine is empty!" + }, + { + "code": 6011, + "name": "CandyMachineNotLive", + "msg": "Candy machine is not live!" + }, + { + "code": 6012, + "name": "HiddenSettingsConfigsDoNotHaveConfigLines", + "msg": "Configs that are using hidden uris do not have config lines, they have a single hash representing hashed order" + }, + { + "code": 6013, + "name": "CannotChangeNumberOfLines", + "msg": "Cannot change number of lines unless is a hidden config" + }, + { + "code": 6014, + "name": "DerivedKeyInvalid", + "msg": "Derived key invalid" + }, + { + "code": 6015, + "name": "PublicKeyMismatch", + "msg": "Public key mismatch" + }, + { + "code": 6016, + "name": "NoWhitelistToken", + "msg": "No whitelist token present" + }, + { + "code": 6017, + "name": "TokenBurnFailed", + "msg": "Token burn failed" + }, + { + "code": 6018, + "name": "GatewayAppMissing", + "msg": "Missing gateway app when required" + }, + { + "code": 6019, + "name": "GatewayTokenMissing", + "msg": "Missing gateway token when required" + }, + { + "code": 6020, + "name": "GatewayTokenExpireTimeInvalid", + "msg": "Invalid gateway token expire time" + }, + { + "code": 6021, + "name": "NetworkExpireFeatureMissing", + "msg": "Missing gateway network expire feature when required" + }, + { + "code": 6022, + "name": "CannotFindUsableConfigLine", + "msg": "Unable to find an unused config line near your random number index" + }, + { + "code": 6023, + "name": "InvalidString", + "msg": "Invalid string" + }, + { + "code": 6024, + "name": "SuspiciousTransaction", + "msg": "Suspicious transaction detected" + }, + { + "code": 6025, + "name": "CannotSwitchToHiddenSettings", + "msg": "Cannot Switch to Hidden Settings after items available is greater than 0" + }, + { + "code": 6026, + "name": "IncorrectSlotHashesPubkey", + "msg": "Incorrect SlotHashes PubKey" + }, + { + "code": 6027, + "name": "IncorrectCollectionAuthority", + "msg": "Incorrect collection NFT authority" + }, + { + "code": 6028, + "name": "MismatchedCollectionPDA", + "msg": "Collection PDA address is invalid" + }, + { + "code": 6029, + "name": "MismatchedCollectionMint", + "msg": "Provided mint account doesn't match collection PDA mint" + }, + { + "code": 6030, + "name": "SlotHashesEmpty", + "msg": "Slot hashes Sysvar is empty" + }, + { + "code": 6031, + "name": "MetadataAccountMustBeEmpty", + "msg": "The metadata account has data in it, and this must be empty to mint a new NFT" + }, + { + "code": 6032, + "name": "MissingSetCollectionDuringMint", + "msg": "Missing set collection during mint IX for Candy Machine with collection set" + }, + { + "code": 6033, + "name": "NoChangingCollectionDuringMint", + "msg": "Can't change collection settings after items have begun to be minted" + }, + { + "code": 6034, + "name": "CandyCollectionRequiresRetainAuthority", + "msg": "Retain authority must be true for Candy Machines with a collection set" + }, + { + "code": 6035, + "name": "GatewayProgramError", + "msg": "Error within Gateway program" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/cashiers_check.json b/basics/anchorpy-main/tests/idls/cashiers_check.json new file mode 100644 index 0000000..0b24577 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/cashiers_check.json @@ -0,0 +1,191 @@ +{ + "version": "0.0.0", + "name": "cashiers_check", + "instructions": [ + { + "name": "createCheck", + "accounts": [ + { + "name": "check", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "checkSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "from", + "isMut": true, + "isSigner": false + }, + { + "name": "to", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "memo", + "type": { + "option": "string" + } + }, + { + "name": "nonce", + "type": "u8" + } + ] + }, + { + "name": "cashCheck", + "accounts": [ + { + "name": "check", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "checkSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "cancelCheck", + "accounts": [ + { + "name": "check", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "checkSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "from", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Check", + "type": { + "kind": "struct", + "fields": [ + { + "name": "from", + "type": "publicKey" + }, + { + "name": "to", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "memo", + "type": { + "option": "string" + } + }, + { + "name": "vault", + "type": "publicKey" + }, + { + "name": "nonce", + "type": "u8" + }, + { + "name": "burned", + "type": "bool" + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "InvalidCheckNonce", + "msg": "The given nonce does not create a valid program derived address." + }, + { + "code": 301, + "name": "InvalidCheckSigner", + "msg": "The derived check signer does not match that which was given." + }, + { + "code": 302, + "name": "AlreadyBurned", + "msg": "The given check has already been burned." + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/chat.json b/basics/anchorpy-main/tests/idls/chat.json new file mode 100644 index 0000000..c911efa --- /dev/null +++ b/basics/anchorpy-main/tests/idls/chat.json @@ -0,0 +1,157 @@ +{ + "version": "0.0.0", + "name": "chat", + "instructions": [ + { + "name": "createUser", + "accounts": [ + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "name", + "type": "string" + } + ] + }, + { + "name": "createChatRoom", + "accounts": [ + { + "name": "chatRoom", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "name", + "type": "string" + } + ] + }, + { + "name": "sendMessage", + "accounts": [ + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "chatRoom", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "msg", + "type": "string" + } + ] + } + ], + "accounts": [ + { + "name": "User", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "authority", + "type": "publicKey" + } + ] + } + }, + { + "name": "ChatRoom", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "tail", + "type": "u64" + }, + { + "name": "name", + "type": { + "array": [ + "u8", + 280 + ] + } + }, + { + "name": "messages", + "type": { + "array": [ + { + "defined": "Message" + }, + 33607 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "Message", + "type": { + "kind": "struct", + "fields": [ + { + "name": "from", + "type": "publicKey" + }, + { + "name": "data", + "type": { + "array": [ + "u8", + 280 + ] + } + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "Unknown" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/clientgen_example_program.json b/basics/anchorpy-main/tests/idls/clientgen_example_program.json new file mode 100644 index 0000000..f5b322d --- /dev/null +++ b/basics/anchorpy-main/tests/idls/clientgen_example_program.json @@ -0,0 +1,737 @@ +{ + "version": "0.1.0", + "name": "example_program", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": true + }, + { + "name": "nested", + "accounts": [ + { + "name": "clock", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "initializeWithValues", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": true + }, + { + "name": "nested", + "accounts": [ + { + "name": "clock", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "boolField", + "type": "bool" + }, + { + "name": "u8Field", + "type": "u8" + }, + { + "name": "i8Field", + "type": "i8" + }, + { + "name": "u16Field", + "type": "u16" + }, + { + "name": "i16Field", + "type": "i16" + }, + { + "name": "u32Field", + "type": "u32" + }, + { + "name": "i32Field", + "type": "i32" + }, + { + "name": "f32Field", + "type": "f32" + }, + { + "name": "u64Field", + "type": "u64" + }, + { + "name": "i64Field", + "type": "i64" + }, + { + "name": "f64Field", + "type": "f64" + }, + { + "name": "u128Field", + "type": "u128" + }, + { + "name": "i128Field", + "type": "i128" + }, + { + "name": "bytesField", + "type": "bytes" + }, + { + "name": "stringField", + "type": "string" + }, + { + "name": "pubkeyField", + "type": "publicKey" + }, + { + "name": "vecField", + "type": { + "vec": "u64" + } + }, + { + "name": "vecStructField", + "type": { + "vec": { + "defined": "FooStruct" + } + } + }, + { + "name": "optionField", + "type": { + "option": "bool" + } + }, + { + "name": "optionStructField", + "type": { + "option": { + "defined": "FooStruct" + } + } + }, + { + "name": "structField", + "type": { + "defined": "FooStruct" + } + }, + { + "name": "arrayField", + "type": { + "array": [ + "bool", + 3 + ] + } + }, + { + "name": "enumField1", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField2", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField3", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField4", + "type": { + "defined": "FooEnum" + } + } + ] + }, + { + "name": "initializeWithValues2", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "vecOfOption", + "type": { + "vec": { + "option": "u64" + } + } + } + ] + }, + { + "name": "causeError", + "accounts": [], + "args": [] + }, + { + "name": "initMyAccount", + "accounts": [ + { + "name": "base", + "isMut": false, + "isSigner": false + }, + { + "name": "base2", + "isMut": false, + "isSigner": false + }, + { + "name": "account", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "another-seed" + }, + { + "kind": "const", + "type": "string", + "value": "test" + }, + { + "kind": "const", + "type": { + "array": [ + "u8", + 2 + ] + }, + "value": [ + 104, + 105 + ] + }, + { + "kind": "const", + "type": "string", + "value": "hi" + }, + { + "kind": "const", + "type": "u8", + "value": 1 + }, + { + "kind": "const", + "type": "u32", + "value": 2 + }, + { + "kind": "const", + "type": "u64", + "value": 3 + } + ] + } + }, + { + "name": "nested", + "accounts": [ + { + "name": "accountNested", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "nested-seed" + }, + { + "kind": "const", + "type": "string", + "value": "test" + }, + { + "kind": "const", + "type": { + "array": [ + "u8", + 2 + ] + }, + "value": [ + 104, + 105 + ] + }, + { + "kind": "const", + "type": "string", + "value": "hi" + }, + { + "kind": "const", + "type": "u8", + "value": 1 + }, + { + "kind": "const", + "type": "u32", + "value": 2 + }, + { + "kind": "const", + "type": "u64", + "value": 3 + } + ] + } + } + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "seedA", + "type": "u8" + } + ] + }, + { + "name": "incrementStateWhenPresent", + "accounts": [ + { + "name": "firstState", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "secondState", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "MyAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": "u64" + } + ] + } + }, + { + "name": "BaseAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "baseData", + "type": "u64" + }, + { + "name": "baseDataKey", + "type": "publicKey" + } + ] + } + }, + { + "name": "State", + "type": { + "kind": "struct", + "fields": [ + { + "name": "boolField", + "type": "bool" + }, + { + "name": "u8Field", + "type": "u8" + }, + { + "name": "i8Field", + "type": "i8" + }, + { + "name": "u16Field", + "type": "u16" + }, + { + "name": "i16Field", + "type": "i16" + }, + { + "name": "u32Field", + "type": "u32" + }, + { + "name": "i32Field", + "type": "i32" + }, + { + "name": "f32Field", + "type": "f32" + }, + { + "name": "u64Field", + "type": "u64" + }, + { + "name": "i64Field", + "type": "i64" + }, + { + "name": "f64Field", + "type": "f64" + }, + { + "name": "u128Field", + "type": "u128" + }, + { + "name": "i128Field", + "type": "i128" + }, + { + "name": "bytesField", + "type": "bytes" + }, + { + "name": "stringField", + "type": "string" + }, + { + "name": "pubkeyField", + "type": "publicKey" + }, + { + "name": "vecField", + "type": { + "vec": "u64" + } + }, + { + "name": "vecStructField", + "type": { + "vec": { + "defined": "FooStruct" + } + } + }, + { + "name": "optionField", + "type": { + "option": "bool" + } + }, + { + "name": "optionStructField", + "type": { + "option": { + "defined": "FooStruct" + } + } + }, + { + "name": "structField", + "type": { + "defined": "FooStruct" + } + }, + { + "name": "arrayField", + "type": { + "array": [ + "bool", + 3 + ] + } + }, + { + "name": "enumField1", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField2", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField3", + "type": { + "defined": "FooEnum" + } + }, + { + "name": "enumField4", + "type": { + "defined": "FooEnum" + } + } + ] + } + }, + { + "name": "State2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vecOfOption", + "type": { + "vec": { + "option": "u64" + } + } + } + ] + } + } + ], + "types": [ + { + "name": "BarStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "someField", + "type": "bool" + }, + { + "name": "otherField", + "type": "u8" + } + ] + } + }, + { + "name": "FooStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "field1", + "type": "u8" + }, + { + "name": "field2", + "type": "u16" + }, + { + "name": "nested", + "type": { + "defined": "BarStruct" + } + }, + { + "name": "vecNested", + "type": { + "vec": { + "defined": "BarStruct" + } + } + }, + { + "name": "optionNested", + "type": { + "option": { + "defined": "BarStruct" + } + } + }, + { + "name": "enumField", + "type": { + "defined": "FooEnum" + } + } + ] + } + }, + { + "name": "FooEnum", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Unnamed", + "fields": [ + "bool", + "u8", + { + "defined": "BarStruct" + } + ] + }, + { + "name": "UnnamedSingle", + "fields": [ + { + "defined": "BarStruct" + } + ] + }, + { + "name": "Named", + "fields": [ + { + "name": "bool_field", + "type": "bool" + }, + { + "name": "u8_field", + "type": "u8" + }, + { + "name": "nested", + "type": { + "defined": "BarStruct" + } + } + ] + }, + { + "name": "Struct", + "fields": [ + { + "defined": "BarStruct" + } + ] + }, + { + "name": "OptionStruct", + "fields": [ + { + "option": { + "defined": "BarStruct" + } + } + ] + }, + { + "name": "VecStruct", + "fields": [ + { + "vec": { + "defined": "BarStruct" + } + } + ] + }, + { + "name": "NoFields" + } + ] + } + } + ], + "errors": [ + { + "code": 6000, + "name": "SomeError", + "msg": "Example error." + }, + { + "code": 6001, + "name": "OtherError", + "msg": "Another error." + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/composite.json b/basics/anchorpy-main/tests/idls/composite.json new file mode 100644 index 0000000..e8d15b7 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/composite.json @@ -0,0 +1,86 @@ +{ + "version": "0.0.0", + "name": "composite", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "dummyA", + "isMut": true, + "isSigner": false + }, + { + "name": "dummyB", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "compositeUpdate", + "accounts": [ + { + "name": "foo", + "accounts": [ + { + "name": "dummyA", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "bar", + "accounts": [ + { + "name": "dummyB", + "isMut": true, + "isSigner": false + } + ] + } + ], + "args": [ + { + "name": "dummyA", + "type": "u64" + }, + { + "name": "dummyB", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "DummyA", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": "u64" + } + ] + } + }, + { + "name": "DummyB", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "address": "FYuVUPk3hmpT44GjCTL6w8Z3kYQoYBAWZMXEi4P63xto" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/counter_auth.json b/basics/anchorpy-main/tests/idls/counter_auth.json new file mode 100644 index 0000000..4ad926a --- /dev/null +++ b/basics/anchorpy-main/tests/idls/counter_auth.json @@ -0,0 +1,5 @@ +{ + "version": "0.0.0", + "name": "counter_auth", + "instructions": [] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/errors.json b/basics/anchorpy-main/tests/idls/errors.json new file mode 100644 index 0000000..346f8a4 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/errors.json @@ -0,0 +1,88 @@ +{ + "version": "0.0.0", + "name": "errors", + "instructions": [ + { + "name": "hello", + "accounts": [], + "args": [] + }, + { + "name": "helloNoMsg", + "accounts": [], + "args": [] + }, + { + "name": "helloNext", + "accounts": [], + "args": [] + }, + { + "name": "mutError", + "accounts": [ + { + "name": "myAccount", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "hasOneError", + "accounts": [ + { + "name": "myAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "signerError", + "accounts": [ + { + "name": "myAccount", + "isMut": false, + "isSigner": true + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "HasOneAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "owner", + "type": "publicKey" + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "Hello", + "msg": "This is an error message clients will automatically display" + }, + { + "code": 423, + "name": "HelloNoMsg" + }, + { + "code": 424, + "name": "HelloNext" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/escrow.json b/basics/anchorpy-main/tests/idls/escrow.json new file mode 100644 index 0000000..5d7c733 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/escrow.json @@ -0,0 +1,161 @@ +{ + "version": "0.0.0", + "name": "escrow", + "instructions": [ + { + "name": "initializeEscrow", + "accounts": [ + { + "name": "initializer", + "isMut": false, + "isSigner": true + }, + { + "name": "initializerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "initializerReceiveTokenAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "initializerAmount", + "type": "u64" + }, + { + "name": "takerAmount", + "type": "u64" + } + ] + }, + { + "name": "cancelEscrow", + "accounts": [ + { + "name": "initializer", + "isMut": false, + "isSigner": false + }, + { + "name": "pdaDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "pdaAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "exchange", + "accounts": [ + { + "name": "taker", + "isMut": false, + "isSigner": true + }, + { + "name": "takerDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "takerReceiveTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "pdaDepositTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "initializerReceiveTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "initializerMainAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "pdaAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "EscrowAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initializerKey", + "type": "publicKey" + }, + { + "name": "initializerDepositTokenAccount", + "type": "publicKey" + }, + { + "name": "initializerReceiveTokenAccount", + "type": "publicKey" + }, + { + "name": "initializerAmount", + "type": "u64" + }, + { + "name": "takerAmount", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "address": "38VPDKPpsXerxe7oWCXGQtm5usFGXrf65tbF3A7cs518" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/events.json b/basics/anchorpy-main/tests/idls/events.json new file mode 100644 index 0000000..3de103d --- /dev/null +++ b/basics/anchorpy-main/tests/idls/events.json @@ -0,0 +1,28 @@ +{ + "version": "0.0.0", + "name": "events", + "instructions": [ + { + "name": "initialize", + "accounts": [], + "args": [] + } + ], + "events": [ + { + "name": "MyEvent", + "fields": [ + { + "name": "data", + "type": "u64", + "index": false + }, + { + "name": "label", + "type": "string", + "index": true + } + ] + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/ido_pool.json b/basics/anchorpy-main/tests/idls/ido_pool.json new file mode 100644 index 0000000..430c4d2 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/ido_pool.json @@ -0,0 +1,397 @@ +{ + "version": "0.0.0", + "name": "ido_pool", + "instructions": [ + { + "name": "initializePool", + "accounts": [ + { + "name": "poolAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "poolSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "redeemableMint", + "isMut": false, + "isSigner": false + }, + { + "name": "usdcMint", + "isMut": false, + "isSigner": false + }, + { + "name": "poolWatermelon", + "isMut": true, + "isSigner": false + }, + { + "name": "poolUsdc", + "isMut": false, + "isSigner": false + }, + { + "name": "distributionAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "creatorWatermelon", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "numIdoTokens", + "type": "u64" + }, + { + "name": "nonce", + "type": "u8" + }, + { + "name": "startIdoTs", + "type": "i64" + }, + { + "name": "endDepositsTs", + "type": "i64" + }, + { + "name": "endIdoTs", + "type": "i64" + } + ] + }, + { + "name": "exchangeUsdcForRedeemable", + "accounts": [ + { + "name": "poolAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "poolSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "redeemableMint", + "isMut": true, + "isSigner": false + }, + { + "name": "poolUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "userAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "userRedeemable", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "exchangeRedeemableForUsdc", + "accounts": [ + { + "name": "poolAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "poolSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "redeemableMint", + "isMut": true, + "isSigner": false + }, + { + "name": "poolUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "userAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "userRedeemable", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "exchangeRedeemableForWatermelon", + "accounts": [ + { + "name": "poolAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "poolSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "redeemableMint", + "isMut": true, + "isSigner": false + }, + { + "name": "poolWatermelon", + "isMut": true, + "isSigner": false + }, + { + "name": "userAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userWatermelon", + "isMut": true, + "isSigner": false + }, + { + "name": "userRedeemable", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdrawPoolUsdc", + "accounts": [ + { + "name": "poolAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "poolSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "poolUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "distributionAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "creatorUsdc", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "PoolAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "redeemableMint", + "type": "publicKey" + }, + { + "name": "poolWatermelon", + "type": "publicKey" + }, + { + "name": "watermelonMint", + "type": "publicKey" + }, + { + "name": "poolUsdc", + "type": "publicKey" + }, + { + "name": "distributionAuthority", + "type": "publicKey" + }, + { + "name": "nonce", + "type": "u8" + }, + { + "name": "numIdoTokens", + "type": "u64" + }, + { + "name": "startIdoTs", + "type": "i64" + }, + { + "name": "endDepositsTs", + "type": "i64" + }, + { + "name": "endIdoTs", + "type": "i64" + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "IdoFuture", + "msg": "IDO must start in the future" + }, + { + "code": 301, + "name": "SeqTimes", + "msg": "IDO times are non-sequential" + }, + { + "code": 302, + "name": "StartIdoTime", + "msg": "IDO has not started" + }, + { + "code": 303, + "name": "EndDepositsTime", + "msg": "Deposits period has ended" + }, + { + "code": 304, + "name": "EndIdoTime", + "msg": "IDO has ended" + }, + { + "code": 305, + "name": "IdoNotOver", + "msg": "IDO has not finished yet" + }, + { + "code": 306, + "name": "LowUsdc", + "msg": "Insufficient USDC" + }, + { + "code": 307, + "name": "LowRedeemable", + "msg": "Insufficient redeemable tokens" + }, + { + "code": 308, + "name": "UsdcNotEqRedeem", + "msg": "USDC total and redeemable total don't match" + }, + { + "code": 309, + "name": "InvalidNonce", + "msg": "Given nonce is invalid" + } + ], + "metadata": { + "address": "6iQsUPMA3k453npuvqbgARkSudA8YEtuDersUW7J5Ak1" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/jet.json b/basics/anchorpy-main/tests/idls/jet.json new file mode 100644 index 0000000..9269654 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/jet.json @@ -0,0 +1,1905 @@ +{ + "version": "0.0.0", + "name": "jet", + "instructions": [ + { + "name": "initMarket", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "quoteCurrency", + "type": "string" + }, + { + "name": "quoteTokenMint", + "type": "publicKey" + } + ] + }, + { + "name": "initReserve", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "feeNoteVault", + "isMut": true, + "isSigner": false + }, + { + "name": "dexSwapTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "dexOpenOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "dexMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "dexProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "oraclePrice", + "isMut": false, + "isSigner": false + }, + { + "name": "oracleProduct", + "isMut": false, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "quoteTokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": { + "defined": "InitReserveBumpSeeds" + } + }, + { + "name": "config", + "type": { + "defined": "ReserveConfig" + } + } + ] + }, + { + "name": "updateReserveConfig", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "newConfig", + "type": { + "defined": "ReserveConfig" + } + } + ] + }, + { + "name": "initDepositAccount", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": false, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": false, + "isSigner": false + }, + { + "name": "depositor", + "isMut": true, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "initCollateralAccount", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "reserve", + "isMut": false, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "collateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "initLoanAccount", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "reserve", + "isMut": false, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "loanAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "initObligation", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "borrower", + "isMut": true, + "isSigner": true + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "setMarketOwner", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "newOwner", + "type": "publicKey" + } + ] + }, + { + "name": "setMarketFlags", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "flags", + "type": "u64" + } + ] + }, + { + "name": "closeDepositAccount", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "depositor", + "isMut": true, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "receiverAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "deposit", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "depositor", + "isMut": false, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "depositSource", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "withdraw", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "depositor", + "isMut": false, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "withdrawAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "depositCollateral", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": { + "defined": "DepositCollateralBumpSeeds" + } + }, + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "withdrawCollateral", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "depositAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": { + "defined": "WithdrawCollateralBumpSeeds" + } + }, + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "borrow", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "borrower", + "isMut": false, + "isSigner": true + }, + { + "name": "loanAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "receiverAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "repay", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "loanAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "payerAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": { + "defined": "Amount" + } + } + ] + }, + { + "name": "liquidate", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralReserve", + "isMut": false, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "loanAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "payerAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "receiverAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": { + "defined": "Amount" + } + }, + { + "name": "minCollateral", + "type": "u64" + } + ] + }, + { + "name": "mockLiquidateDex", + "accounts": [ + { + "name": "sourceMarket", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "openOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "requestQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "eventQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "bids", + "isMut": true, + "isSigner": false + }, + { + "name": "asks", + "isMut": true, + "isSigner": false + }, + { + "name": "coinVault", + "isMut": true, + "isSigner": false + }, + { + "name": "pcVault", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultSigner", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "targetMarket", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "openOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "requestQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "eventQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "bids", + "isMut": true, + "isSigner": false + }, + { + "name": "asks", + "isMut": true, + "isSigner": false + }, + { + "name": "coinVault", + "isMut": true, + "isSigner": false + }, + { + "name": "pcVault", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultSigner", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "toLiquidate", + "accounts": [ + { + "name": "market", + "isMut": false, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "obligation", + "isMut": true, + "isSigner": false + }, + { + "name": "loanReserve", + "isMut": true, + "isSigner": false + }, + { + "name": "loanReserveVault", + "isMut": true, + "isSigner": false + }, + { + "name": "loanNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "loanAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralReserve", + "isMut": false, + "isSigner": false + }, + { + "name": "collateralReserveVault", + "isMut": true, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "dexSwapTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "dexProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ] + } + ], + "args": [] + }, + { + "name": "refreshReserve", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "marketAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "reserve", + "isMut": true, + "isSigner": false + }, + { + "name": "feeNoteVault", + "isMut": true, + "isSigner": false + }, + { + "name": "depositNoteMint", + "isMut": true, + "isSigner": false + }, + { + "name": "pythOraclePrice", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Market", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": "u32" + }, + { + "name": "quoteExponent", + "type": "i32" + }, + { + "name": "quoteCurrency", + "type": { + "array": [ + "u8", + 15 + ] + } + }, + { + "name": "authorityBumpSeed", + "type": { + "array": [ + "u8", + 1 + ] + } + }, + { + "name": "authoritySeed", + "type": "publicKey" + }, + { + "name": "marketAuthority", + "type": "publicKey" + }, + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "quoteTokenMint", + "type": "publicKey" + }, + { + "name": "flags", + "type": "u64" + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 352 + ] + } + }, + { + "name": "reserves", + "type": { + "array": [ + "u8", + 12288 + ] + } + } + ] + } + }, + { + "name": "Obligation", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": "u32" + }, + { + "name": "reserved0", + "type": "u32" + }, + { + "name": "market", + "type": "publicKey" + }, + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "reserved1", + "type": { + "array": [ + "u8", + 184 + ] + } + }, + { + "name": "cached", + "type": { + "array": [ + "u8", + 256 + ] + } + }, + { + "name": "collateral", + "type": { + "array": [ + "u8", + 2048 + ] + } + }, + { + "name": "loans", + "type": { + "array": [ + "u8", + 2048 + ] + } + } + ] + } + }, + { + "name": "Reserve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": "u16" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "exponent", + "type": "i32" + }, + { + "name": "market", + "type": "publicKey" + }, + { + "name": "pythOraclePrice", + "type": "publicKey" + }, + { + "name": "pythOracleProduct", + "type": "publicKey" + }, + { + "name": "tokenMint", + "type": "publicKey" + }, + { + "name": "depositNoteMint", + "type": "publicKey" + }, + { + "name": "loanNoteMint", + "type": "publicKey" + }, + { + "name": "vault", + "type": "publicKey" + }, + { + "name": "feeNoteVault", + "type": "publicKey" + }, + { + "name": "dexSwapTokens", + "type": "publicKey" + }, + { + "name": "dexOpenOrders", + "type": "publicKey" + }, + { + "name": "dexMarket", + "type": "publicKey" + }, + { + "name": "reserved0", + "type": { + "array": [ + "u8", + 408 + ] + } + }, + { + "name": "config", + "type": { + "defined": "ReserveConfig" + } + }, + { + "name": "reserved1", + "type": { + "array": [ + "u8", + 704 + ] + } + }, + { + "name": "state", + "type": { + "array": [ + "u8", + 512 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "Amount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "units", + "type": { + "defined": "AmountUnits" + } + }, + { + "name": "value", + "type": "u64" + } + ] + } + }, + { + "name": "DepositCollateralBumpSeeds", + "type": { + "kind": "struct", + "fields": [ + { + "name": "collateralAccount", + "type": "u8" + }, + { + "name": "depositAccount", + "type": "u8" + } + ] + } + }, + { + "name": "InitReserveBumpSeeds", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vault", + "type": "u8" + }, + { + "name": "feeNoteVault", + "type": "u8" + }, + { + "name": "dexOpenOrders", + "type": "u8" + }, + { + "name": "dexSwapTokens", + "type": "u8" + }, + { + "name": "depositNoteMint", + "type": "u8" + }, + { + "name": "loanNoteMint", + "type": "u8" + } + ] + } + }, + { + "name": "ReserveConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "utilizationRate1", + "type": "u16" + }, + { + "name": "utilizationRate2", + "type": "u16" + }, + { + "name": "borrowRate0", + "type": "u16" + }, + { + "name": "borrowRate1", + "type": "u16" + }, + { + "name": "borrowRate2", + "type": "u16" + }, + { + "name": "borrowRate3", + "type": "u16" + }, + { + "name": "minCollateralRatio", + "type": "u16" + }, + { + "name": "liquidationPremium", + "type": "u16" + }, + { + "name": "manageFeeCollectionThreshold", + "type": "u64" + }, + { + "name": "manageFeeRate", + "type": "u16" + }, + { + "name": "loanOriginationFee", + "type": "u16" + }, + { + "name": "liquidationSlippage", + "type": "u16" + }, + { + "name": "reserved0", + "type": "u16" + }, + { + "name": "liquidationDexTradeMax", + "type": "u64" + }, + { + "name": "reserved1", + "type": { + "array": [ + "u8", + 24 + ] + } + } + ] + } + }, + { + "name": "WithdrawCollateralBumpSeeds", + "type": { + "kind": "struct", + "fields": [ + { + "name": "collateralAccount", + "type": "u8" + }, + { + "name": "depositAccount", + "type": "u8" + } + ] + } + }, + { + "name": "CacheInvalidError", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Expired", + "fields": [ + { + "name": "msg", + "type": "string" + } + ] + }, + { + "name": "TooNew", + "fields": [ + { + "name": "msg", + "type": "string" + } + ] + }, + { + "name": "Invalidated" + } + ] + } + }, + { + "name": "AmountUnits", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Tokens" + }, + { + "name": "DepositNotes" + }, + { + "name": "LoanNotes" + } + ] + } + }, + { + "name": "Rounding", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Up" + }, + { + "name": "Down" + } + ] + } + }, + { + "name": "DexSide", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Bid" + }, + { + "name": "Ask" + } + ] + } + }, + { + "name": "Side", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Collateral" + }, + { + "name": "Loan" + } + ] + } + }, + { + "name": "JobCompletion", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Partial" + }, + { + "name": "Full" + } + ] + } + } + ], + "events": [ + { + "name": "BorrowEvent", + "fields": [ + { + "name": "borrower", + "type": "publicKey", + "index": false + }, + { + "name": "reserve", + "type": "publicKey", + "index": false + }, + { + "name": "debt", + "type": "u64", + "index": false + } + ] + }, + { + "name": "DepositCollateralEvent", + "fields": [ + { + "name": "depositor", + "type": "publicKey", + "index": false + }, + { + "name": "reserve", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": { + "defined": "Amount" + }, + "index": false + } + ] + }, + { + "name": "LiquidateEvent", + "fields": [ + { + "name": "borrower", + "type": "publicKey", + "index": false + }, + { + "name": "debtReserve", + "type": "publicKey", + "index": false + }, + { + "name": "collateralReserve", + "type": "publicKey", + "index": false + }, + { + "name": "paidAmount", + "type": { + "defined": "Amount" + }, + "index": false + }, + { + "name": "collateralAmount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "RepayEvent", + "fields": [ + { + "name": "borrower", + "type": "publicKey", + "index": false + }, + { + "name": "reserve", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": { + "defined": "Amount" + }, + "index": false + } + ] + }, + { + "name": "WithdrawCollateralEvent", + "fields": [ + { + "name": "depositor", + "type": "publicKey", + "index": false + }, + { + "name": "reserve", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": { + "defined": "Amount" + }, + "index": false + } + ] + } + ], + "errors": [ + { + "code": 300, + "name": "ArithmeticError", + "msg": "failed to perform some math operation safely" + }, + { + "code": 301, + "name": "InvalidOracle", + "msg": "oracle account provided is not valid" + }, + { + "code": 302, + "name": "NoFreeReserves", + "msg": "no free space left to add a new reserve in the market" + }, + { + "code": 303, + "name": "NoFreeObligation", + "msg": "no free space left to add the new loan or collateral in an obligation" + }, + { + "code": 304, + "name": "UnregisteredPosition", + "msg": "the obligation account doesn't have any record of the loan or collateral account" + }, + { + "code": 305, + "name": "InvalidOraclePrice", + "msg": "the oracle price account has an invalid price value" + }, + { + "code": 306, + "name": "InsufficientCollateral", + "msg": "there is not enough collateral deposited to borrow against" + }, + { + "code": 307, + "name": "SimultaneousDepositAndBorrow", + "msg": "cannot both deposit collateral to and borrow from the same reserve" + }, + { + "code": 308, + "name": "ObligationHealthy", + "msg": "cannot liquidate a healthy position" + }, + { + "code": 309, + "name": "ObligationUnhealthy", + "msg": "cannot perform an action that would leave the obligation unhealthy" + }, + { + "code": 310, + "name": "ExceptionalReserveState", + "msg": "reserve requires special action; call refresh_reserve until up to date" + }, + { + "code": 311, + "name": "InvalidAmountUnits", + "msg": "the units provided in the amount are not valid for the instruction" + }, + { + "code": 312, + "name": "InvalidDexMarketMints", + "msg": "the tokens in the DEX market don't match the reserve and lending market quote token" + }, + { + "code": 313, + "name": "InvalidMarketAuthority", + "msg": "the market authority provided doesn't match the market account" + }, + { + "code": 314, + "name": "InvalidLiquidationQuoteTokenAccount", + "msg": "the quote token account provided cannot be used for liquidations" + }, + { + "code": 315, + "name": "ObligationAccountMismatch", + "msg": "the obligation account doesn't have the collateral/loan registered" + }, + { + "code": 316, + "name": "UnknownInstruction", + "msg": "unknown instruction" + }, + { + "code": 317, + "name": "Disallowed", + "msg": "current conditions prevent an action from being performed" + }, + { + "code": 318, + "name": "LiquidationSwapSlipped", + "msg": "the actual slipped amount on the DEX trade exceeded the threshold configured" + }, + { + "code": 319, + "name": "CollateralValueTooSmall", + "msg": "the collateral value is too small for a DEX trade" + }, + { + "code": 320, + "name": "LiquidationLowCollateral", + "msg": "the collateral returned by the liquidation is smaller than requested" + }, + { + "code": 321, + "name": "NotSupported", + "msg": "this action is currently not supported by this version of the program" + }, + { + "code": 322, + "name": "MarketHalted", + "msg": "the market has currently halted this kind of operation" + }, + { + "code": 323, + "name": "InvalidParameter", + "msg": "a given parameter is not valid" + } + ], + "metadata": { + "address": "JPv1rCqrhagNNmJVM5J1he7msQ5ybtvE1nNuHpDHMNU" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/jet_auth.json b/basics/anchorpy-main/tests/idls/jet_auth.json new file mode 100644 index 0000000..337d251 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/jet_auth.json @@ -0,0 +1,99 @@ +{ + "version": "1.0.0", + "name": "jet_auth", + "instructions": [ + { + "name": "createUserAuth", + "accounts": [ + { + "name": "user", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "auth", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "authenticate", + "accounts": [ + { + "name": "auth", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + } + ], + "accounts": [ + { + "name": "UserAuthentication", + "type": { + "kind": "struct", + "fields": [ + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "allowed", + "type": "bool" + } + ] + } + } + ], + "events": [ + { + "name": "AuthAccountCreated", + "fields": [ + { + "name": "user", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "Authenticated", + "fields": [ + { + "name": "user", + "type": "publicKey", + "index": false + } + ] + } + ] +} diff --git a/basics/anchorpy-main/tests/idls/jet_rewards.json b/basics/anchorpy-main/tests/idls/jet_rewards.json new file mode 100644 index 0000000..6894a17 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/jet_rewards.json @@ -0,0 +1,1177 @@ +{ + "version": "1.0.0", + "name": "jet_rewards", + "constants": [ + { + "name": "AWARD", + "type": { + "defined": "&[u8]" + }, + "value": "b\"award\"" + }, + { + "name": "DISTRIBUTION", + "type": { + "defined": "&[u8]" + }, + "value": "b\"distribution\"" + }, + { + "name": "VAULT", + "type": { + "defined": "&[u8]" + }, + "value": "b\"vault\"" + } + ], + "instructions": [ + { + "name": "airdropCreate", + "accounts": [ + { + "name": "airdrop", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "rewardVault", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AirdropCreateParams" + } + } + ], + "returns": null + }, + { + "name": "airdropAddRecipients", + "accounts": [ + { + "name": "airdrop", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AirdropAddRecipientsParams" + } + } + ], + "returns": null + }, + { + "name": "airdropFinalize", + "accounts": [ + { + "name": "airdrop", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardVault", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [], + "returns": null + }, + { + "name": "airdropClose", + "accounts": [ + { + "name": "airdrop", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardVault", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenReceiver", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "airdropClaim", + "accounts": [ + { + "name": "airdrop", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardVault", + "isMut": true, + "isSigner": false + }, + { + "name": "recipient", + "isMut": false, + "isSigner": true + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "stakePool", + "isMut": true, + "isSigner": false + }, + { + "name": "stakePoolVault", + "isMut": true, + "isSigner": false + }, + { + "name": "stakeAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "voterWeightRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "maxVoterWeightRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "stakingProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "distributionCreate", + "accounts": [ + { + "name": "distribution", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "payerRent", + "isMut": true, + "isSigner": true + }, + { + "name": "payerTokenAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "payerTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "DistributionCreateParams" + } + } + ], + "returns": null + }, + { + "name": "distributionRelease", + "accounts": [ + { + "name": "distribution", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "targetAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "distributionClose", + "accounts": [ + { + "name": "distribution", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "awardCreate", + "accounts": [ + { + "name": "award", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenSource", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenSourceAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "payerRent", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AwardCreateParams" + } + } + ], + "returns": null + }, + { + "name": "awardRelease", + "accounts": [ + { + "name": "award", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "stakeAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "voterWeightRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "maxVoterWeightRecord", + "isMut": true, + "isSigner": false + }, + { + "name": "stakePool", + "isMut": true, + "isSigner": false + }, + { + "name": "stakePoolVault", + "isMut": true, + "isSigner": false + }, + { + "name": "stakingProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "awardClose", + "accounts": [ + { + "name": "award", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + }, + { + "name": "awardRevoke", + "accounts": [ + { + "name": "award", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenReceiver", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "returns": null + } + ], + "accounts": [ + { + "name": "Airdrop", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "rewardVault", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "expireAt", + "type": "i64" + }, + { + "name": "stakePool", + "type": "publicKey" + }, + { + "name": "flags", + "type": "u64" + }, + { + "name": "shortDesc", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "longDesc", + "type": { + "array": ["u8", 255] + } + }, + { + "name": "vaultBump", + "type": { + "array": ["u8", 1] + } + }, + { + "name": "targetInfo", + "type": { + "array": ["u8", 400024] + } + } + ] + } + }, + { + "name": "Award", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "seed", + "type": { + "array": ["u8", 30] + } + }, + { + "name": "seedLen", + "type": "u8" + }, + { + "name": "bumpSeed", + "type": { + "array": ["u8", 1] + } + }, + { + "name": "stakeAccount", + "type": "publicKey" + }, + { + "name": "vault", + "type": "publicKey" + }, + { + "name": "tokenDistribution", + "type": { + "defined": "TokenDistribution" + } + } + ] + } + }, + { + "name": "Distribution", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "vault", + "type": "publicKey" + }, + { + "name": "seed", + "type": { + "array": ["u8", 30] + } + }, + { + "name": "seedLen", + "type": "u8" + }, + { + "name": "bumpSeed", + "type": { + "array": ["u8", 1] + } + }, + { + "name": "targetAccount", + "type": "publicKey" + }, + { + "name": "tokenDistribution", + "type": { + "defined": "TokenDistribution" + } + } + ] + } + } + ], + "types": [ + { + "name": "AirdropAddRecipientsParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "startIndex", + "type": "u64" + }, + { + "name": "recipients", + "type": { + "vec": { + "defined": "AirdropRecipientParam" + } + } + } + ] + } + }, + { + "name": "AirdropRecipientParam", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "recipient", + "type": "publicKey" + } + ] + } + }, + { + "name": "AirdropCreateParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "expireAt", + "type": "i64" + }, + { + "name": "stakePool", + "type": "publicKey" + }, + { + "name": "shortDesc", + "type": "string" + }, + { + "name": "longDesc", + "type": "string" + }, + { + "name": "flags", + "type": "u64" + } + ] + } + }, + { + "name": "AwardCreateParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "seed", + "type": "string" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "stakeAccount", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "beginAt", + "type": "u64" + }, + { + "name": "endAt", + "type": "u64" + } + ] + } + }, + { + "name": "DistributionCreateParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "seed", + "type": "string" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "targetAccount", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "beginAt", + "type": "u64" + }, + { + "name": "endAt", + "type": "u64" + } + ] + } + }, + { + "name": "TokenDistribution", + "type": { + "kind": "struct", + "fields": [ + { + "name": "targetAmount", + "type": "u64" + }, + { + "name": "distributed", + "type": "u64" + }, + { + "name": "beginAt", + "type": "u64" + }, + { + "name": "endAt", + "type": "u64" + }, + { + "name": "kind", + "type": { + "defined": "DistributionKind" + } + } + ] + } + }, + { + "name": "DistributionKind", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Linear" + } + ] + } + } + ], + "events": [ + { + "name": "AirdropCreated", + "fields": [ + { + "name": "airdrop", + "type": "publicKey", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "tokenMint", + "type": "publicKey", + "index": false + }, + { + "name": "params", + "type": { + "defined": "AirdropCreateParams" + }, + "index": false + } + ] + }, + { + "name": "AirdropRecipientsAdded", + "fields": [ + { + "name": "airdrop", + "type": "publicKey", + "index": false + }, + { + "name": "rewardAdditional", + "type": "u64", + "index": false + }, + { + "name": "rewardTotal", + "type": "u64", + "index": false + }, + { + "name": "recipientsAdditional", + "type": "u64", + "index": false + }, + { + "name": "recipientsTotal", + "type": "u64", + "index": false + }, + { + "name": "recipients", + "type": { + "vec": { + "defined": "AirdropRecipientParam" + } + }, + "index": false + } + ] + }, + { + "name": "AirdropFinalized", + "fields": [ + { + "name": "airdrop", + "type": "publicKey", + "index": false + }, + { + "name": "rewardTotal", + "type": "u64", + "index": false + }, + { + "name": "recipientsTotal", + "type": "u64", + "index": false + }, + { + "name": "vaultBalance", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AirdropClaimed", + "fields": [ + { + "name": "airdrop", + "type": "publicKey", + "index": false + }, + { + "name": "recipient", + "type": "publicKey", + "index": false + }, + { + "name": "claimedAmount", + "type": "u64", + "index": false + }, + { + "name": "remainingAmount", + "type": "u64", + "index": false + }, + { + "name": "vaultBalance", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AirdropClosed", + "fields": [ + { + "name": "airdrop", + "type": "publicKey", + "index": false + }, + { + "name": "vaultAmount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AwardCreated", + "fields": [ + { + "name": "award", + "type": "publicKey", + "index": false + }, + { + "name": "tokenMint", + "type": "publicKey", + "index": false + }, + { + "name": "params", + "type": { + "defined": "AwardCreateParams" + }, + "index": false + }, + { + "name": "distributionKind", + "type": { + "defined": "DistributionKind" + }, + "index": false + } + ] + }, + { + "name": "AwardReleased", + "fields": [ + { + "name": "award", + "type": "publicKey", + "index": false + }, + { + "name": "amountReleased", + "type": "u64", + "index": false + }, + { + "name": "totalReleased", + "type": "u64", + "index": false + }, + { + "name": "vaultBalance", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AwardRevoked", + "fields": [ + { + "name": "award", + "type": "publicKey", + "index": false + }, + { + "name": "unreleasedAmount", + "type": "u64", + "index": false + }, + { + "name": "totalReleased", + "type": "u64", + "index": false + }, + { + "name": "vaultAmount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AwardClosed", + "fields": [ + { + "name": "award", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "DistributionCreated", + "fields": [ + { + "name": "distribution", + "type": "publicKey", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "tokenMint", + "type": "publicKey", + "index": false + }, + { + "name": "params", + "type": { + "defined": "DistributionCreateParams" + }, + "index": false + }, + { + "name": "distributionKind", + "type": { + "defined": "DistributionKind" + }, + "index": false + } + ] + }, + { + "name": "DistributionReleased", + "fields": [ + { + "name": "distribution", + "type": "publicKey", + "index": false + }, + { + "name": "amountReleased", + "type": "u64", + "index": false + }, + { + "name": "totalDistributed", + "type": "u64", + "index": false + }, + { + "name": "vaultBalance", + "type": "u64", + "index": false + } + ] + }, + { + "name": "DistributionClosed", + "fields": [ + { + "name": "distribution", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 13000, + "name": "RecipientNotFound" + }, + { + "code": 13001, + "name": "AddOutOfOrder" + }, + { + "code": 13002, + "name": "AirdropFinal" + }, + { + "code": 13003, + "name": "AirdropInsufficientRewardBalance" + }, + { + "code": 13004, + "name": "AirdropExpired" + }, + { + "code": 13005, + "name": "AirdropNotFinal" + }, + { + "code": 13006, + "name": "RecipientsNotSorted" + }, + { + "code": 13007, + "name": "DistributionNotEnded" + }, + { + "code": 13008, + "name": "AwardNotFullyVested" + } + ] +} diff --git a/basics/anchorpy-main/tests/idls/merkle_distributor.json b/basics/anchorpy-main/tests/idls/merkle_distributor.json new file mode 100644 index 0000000..d68ecc8 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/merkle_distributor.json @@ -0,0 +1,480 @@ +{ + "version": "0.0.0", + "name": "merkle_distributor", + "instructions": [ + { + "name": "newDistributor", + "accounts": [ + { + "name": "base", + "isMut": false, + "isSigner": true + }, + { + "name": "distributor", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "temporal", + "type": "publicKey" + } + ] + }, + { + "name": "closeDistributor", + "accounts": [ + { + "name": "base", + "isMut": false, + "isSigner": true + }, + { + "name": "distributor", + "isMut": true, + "isSigner": false + }, + { + "name": "distributorWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "receiver", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "walletBump", + "type": "u8" + } + ] + }, + { + "name": "claim", + "accounts": [ + { + "name": "distributor", + "isMut": true, + "isSigner": false + }, + { + "name": "claimStatus", + "isMut": true, + "isSigner": false + }, + { + "name": "from", + "isMut": true, + "isSigner": false + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "temporal", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "index", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "claimantSecret", + "type": "publicKey" + }, + { + "name": "proof", + "type": { + "vec": { + "array": [ + "u8", + 32 + ] + } + } + } + ] + }, + { + "name": "claimCandy", + "accounts": [ + { + "name": "distributor", + "isMut": true, + "isSigner": false + }, + { + "name": "distributorWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "claimCount", + "isMut": true, + "isSigner": false + }, + { + "name": "temporal", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "candyMachineConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "candyMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "candyMachineWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "candyMachineMint", + "isMut": true, + "isSigner": false + }, + { + "name": "candyMachineMetadata", + "isMut": true, + "isSigner": false + }, + { + "name": "candyMachineMasterEdition", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "candyMachineProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "clock", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "walletBump", + "type": "u8" + }, + { + "name": "claimBump", + "type": "u8" + }, + { + "name": "index", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "claimantSecret", + "type": "publicKey" + }, + { + "name": "proof", + "type": { + "vec": { + "array": [ + "u8", + 32 + ] + } + } + } + ] + } + ], + "accounts": [ + { + "name": "MerkleDistributor", + "type": { + "kind": "struct", + "fields": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "temporal", + "type": "publicKey" + } + ] + } + }, + { + "name": "ClaimStatus", + "type": { + "kind": "struct", + "fields": [ + { + "name": "isClaimed", + "type": "bool" + }, + { + "name": "claimant", + "type": "publicKey" + }, + { + "name": "claimedAt", + "type": "i64" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimCount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "count", + "type": "u64" + }, + { + "name": "claimant", + "type": "publicKey" + } + ] + } + }, + { + "name": "CandyMachine", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "wallet", + "type": "publicKey" + }, + { + "name": "tokenMint", + "type": { + "option": "publicKey" + } + }, + { + "name": "config", + "type": "publicKey" + }, + { + "name": "data", + "type": { + "defined": "CandyMachineData" + } + }, + { + "name": "itemsRedeemed", + "type": "u64" + }, + { + "name": "bump", + "type": "u8" + } + ] + } + } + ], + "types": [ + { + "name": "CandyMachineData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "uuid", + "type": "string" + }, + { + "name": "price", + "type": "u64" + }, + { + "name": "itemsAvailable", + "type": "u64" + }, + { + "name": "goLiveDate", + "type": { + "option": "i64" + } + } + ] + } + } + ], + "events": [ + { + "name": "ClaimedEvent", + "fields": [ + { + "name": "index", + "type": "u64", + "index": false + }, + { + "name": "claimant", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 300, + "name": "InvalidProof", + "msg": "Invalid Merkle proof." + }, + { + "code": 301, + "name": "DropAlreadyClaimed", + "msg": "Drop already claimed." + }, + { + "code": 302, + "name": "Unauthorized", + "msg": "Account is not authorized to execute this instruction" + }, + { + "code": 303, + "name": "OwnerMismatch", + "msg": "Token account owner did not match intended owner" + }, + { + "code": 304, + "name": "TemporalMismatch", + "msg": "Temporal signer did not match distributor" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/multisig.json b/basics/anchorpy-main/tests/idls/multisig.json new file mode 100644 index 0000000..8b825b9 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/multisig.json @@ -0,0 +1,277 @@ +{ + "version": "0.0.0", + "name": "multisig", + "instructions": [ + { + "name": "createMultisig", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + }, + { + "name": "threshold", + "type": "u64" + }, + { + "name": "nonce", + "type": "u8" + } + ] + }, + { + "name": "createTransaction", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + }, + { + "name": "proposer", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "pid", + "type": "publicKey" + }, + { + "name": "accs", + "type": { + "vec": { + "defined": "TransactionAccount" + } + } + }, + { + "name": "data", + "type": "bytes" + } + ] + }, + { + "name": "approve", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "setOwners", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + } + ] + }, + { + "name": "changeThreshold", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "threshold", + "type": "u64" + } + ] + }, + { + "name": "executeTransaction", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Multisig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + }, + { + "name": "threshold", + "type": "u64" + }, + { + "name": "nonce", + "type": "u8" + } + ] + } + }, + { + "name": "Transaction", + "type": { + "kind": "struct", + "fields": [ + { + "name": "multisig", + "type": "publicKey" + }, + { + "name": "programId", + "type": "publicKey" + }, + { + "name": "accounts", + "type": { + "vec": { + "defined": "TransactionAccount" + } + } + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "signers", + "type": { + "vec": "bool" + } + }, + { + "name": "didExecute", + "type": "bool" + } + ] + } + } + ], + "types": [ + { + "name": "TransactionAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "isSigner", + "type": "bool" + }, + { + "name": "isWritable", + "type": "bool" + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "InvalidOwner", + "msg": "The given owner is not part of this multisig." + }, + { + "code": 301, + "name": "NotEnoughSigners", + "msg": "Not enough owners signed this transaction." + }, + { + "code": 302, + "name": "TransactionAlreadySigned", + "msg": "Cannot delete a transaction that has been signed by an owner." + }, + { + "code": 303, + "name": "Overflow", + "msg": "Overflow when adding." + }, + { + "code": 304, + "name": "UnableToDelete", + "msg": "Cannot delete a transaction the owner did not create." + }, + { + "code": 305, + "name": "AlreadyExecuted", + "msg": "The given transaction has already been executed." + }, + { + "code": 306, + "name": "InvalidThreshold", + "msg": "Threshold must be less than or equal to the number of owners." + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/puppet.json b/basics/anchorpy-main/tests/idls/puppet.json new file mode 100644 index 0000000..625b61e --- /dev/null +++ b/basics/anchorpy-main/tests/idls/puppet.json @@ -0,0 +1,55 @@ +{ + "version": "0.0.0", + "name": "puppet", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "puppet", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setData", + "accounts": [ + { + "name": "puppet", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "Puppet", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "address": "61Q5bAAha3NF2gowifnZxaRttM1eyCs4Y3j9YjgMWrqb" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/puppet_master.json b/basics/anchorpy-main/tests/idls/puppet_master.json new file mode 100644 index 0000000..186d86e --- /dev/null +++ b/basics/anchorpy-main/tests/idls/puppet_master.json @@ -0,0 +1,30 @@ +{ + "version": "0.0.0", + "name": "puppet_master", + "instructions": [ + { + "name": "pullStrings", + "accounts": [ + { + "name": "puppet", + "isMut": true, + "isSigner": false + }, + { + "name": "puppetProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "data", + "type": "u64" + } + ] + } + ], + "metadata": { + "address": "3UDzsDD9WWPAgHASFHyFdqZz1hFmWpWVCZzLPZabDJxe" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/pyth.json b/basics/anchorpy-main/tests/idls/pyth.json new file mode 100644 index 0000000..0516aaf --- /dev/null +++ b/basics/anchorpy-main/tests/idls/pyth.json @@ -0,0 +1,99 @@ +{ + "version": "0.0.0", + "name": "pyth", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "price", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "price", + "type": "i64" + }, + { + "name": "expo", + "type": "i32" + }, + { + "name": "conf", + "type": "u64" + } + ] + }, + { + "name": "setPrice", + "accounts": [ + { + "name": "price", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "price", + "type": "i64" + } + ] + } + ], + "types": [ + { + "name": "PriceStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Unknown" + }, + { + "name": "Trading" + }, + { + "name": "Halted" + }, + { + "name": "Auction" + } + ] + } + }, + { + "name": "CorpAction", + "type": { + "kind": "enum", + "variants": [ + { + "name": "NoCorpAct" + } + ] + } + }, + { + "name": "PriceType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Unknown" + }, + { + "name": "Price" + }, + { + "name": "TWAP" + }, + { + "name": "Volatility" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/quarry_mine.json b/basics/anchorpy-main/tests/idls/quarry_mine.json new file mode 100644 index 0000000..0d5b494 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/quarry_mine.json @@ -0,0 +1,980 @@ +{ + "version": "0.0.0", + "name": "quarry_mine", + "instructions": [ + { + "name": "newRewarder", + "accounts": [ + { + "name": "base", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "unusedClock", + "isMut": false, + "isSigner": false + }, + { + "name": "mintWrapper", + "isMut": false, + "isSigner": false + }, + { + "name": "rewardsTokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "claimFeeTokenAccount", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "setPauseAuthority", + "accounts": [ + { + "name": "auth", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "pauseAuthority", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "pause", + "accounts": [ + { + "name": "pauseAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "unpause", + "accounts": [ + { + "name": "pauseAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "transferAuthority", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "newAuthority", + "type": "publicKey" + } + ] + }, + { + "name": "acceptAuthority", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setAnnualRewards", + "accounts": [ + { + "name": "auth", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ] + } + ], + "args": [ + { + "name": "newRate", + "type": "u64" + } + ] + }, + { + "name": "createQuarry", + "accounts": [ + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "auth", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "unusedClock", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "setRewardsShare", + "accounts": [ + { + "name": "auth", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "newShare", + "type": "u64" + } + ] + }, + { + "name": "setFamine", + "accounts": [ + { + "name": "auth", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "famineTs", + "type": "i64" + } + ] + }, + { + "name": "updateQuarryRewards", + "accounts": [ + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "createMiner", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenMint", + "isMut": false, + "isSigner": false + }, + { + "name": "minerVault", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "bump", + "type": "u8" + } + ] + }, + { + "name": "claimRewards", + "accounts": [ + { + "name": "mintWrapper", + "isMut": true, + "isSigner": false + }, + { + "name": "mintWrapperProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "minter", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardsTokenMint", + "isMut": true, + "isSigner": false + }, + { + "name": "rewardsTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "claimFeeTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "stake", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "unusedMinerVault", + "isMut": true, + "isSigner": false + }, + { + "name": "unusedTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + } + ] + } + ], + "args": [] + }, + { + "name": "stakeTokens", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "minerVault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdrawTokens", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "quarry", + "isMut": true, + "isSigner": false + }, + { + "name": "minerVault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rewarder", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "extractFees", + "accounts": [ + { + "name": "rewarder", + "isMut": false, + "isSigner": false + }, + { + "name": "claimFeeTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "feeToTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Rewarder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "base", + "type": "publicKey" + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "pendingAuthority", + "type": "publicKey" + }, + { + "name": "numQuarries", + "type": "u16" + }, + { + "name": "annualRewardsRate", + "type": "u64" + }, + { + "name": "totalRewardsShares", + "type": "u64" + }, + { + "name": "mintWrapper", + "type": "publicKey" + }, + { + "name": "rewardsTokenMint", + "type": "publicKey" + }, + { + "name": "claimFeeTokenAccount", + "type": "publicKey" + }, + { + "name": "maxClaimFeeMillibps", + "type": "u64" + }, + { + "name": "pauseAuthority", + "type": "publicKey" + }, + { + "name": "isPaused", + "type": "bool" + } + ] + } + }, + { + "name": "Quarry", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rewarderKey", + "type": "publicKey" + }, + { + "name": "tokenMintKey", + "type": "publicKey" + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "tokenMintDecimals", + "type": "u8" + }, + { + "name": "famineTs", + "type": "i64" + }, + { + "name": "lastUpdateTs", + "type": "i64" + }, + { + "name": "rewardsPerTokenStored", + "type": "u128" + }, + { + "name": "annualRewardsRate", + "type": "u64" + }, + { + "name": "rewardsShare", + "type": "u64" + }, + { + "name": "totalTokensDeposited", + "type": "u64" + }, + { + "name": "numMiners", + "type": "u64" + } + ] + } + }, + { + "name": "Miner", + "type": { + "kind": "struct", + "fields": [ + { + "name": "quarryKey", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "tokenVaultKey", + "type": "publicKey" + }, + { + "name": "rewardsEarned", + "type": "u64" + }, + { + "name": "rewardsPerTokenPaid", + "type": "u128" + }, + { + "name": "balance", + "type": "u64" + }, + { + "name": "index", + "type": "u64" + } + ] + } + } + ], + "types": [ + { + "name": "StakeAction", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Stake" + }, + { + "name": "Withdraw" + } + ] + } + } + ], + "events": [ + { + "name": "NewRewarderEvent", + "fields": [ + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "ClaimEvent", + "fields": [ + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "stakedToken", + "type": "publicKey", + "index": false + }, + { + "name": "rewardsToken", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "fees", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "StakeEvent", + "fields": [ + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "token", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "WithdrawEvent", + "fields": [ + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "token", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "RewarderAnnualRewardsUpdateEvent", + "fields": [ + { + "name": "previousRate", + "type": "u64", + "index": false + }, + { + "name": "newRate", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "MinerCreateEvent", + "fields": [ + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "quarry", + "type": "publicKey", + "index": false + }, + { + "name": "miner", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "QuarryCreateEvent", + "fields": [ + { + "name": "tokenMint", + "type": "publicKey", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "QuarryRewardsUpdateEvent", + "fields": [ + { + "name": "tokenMint", + "type": "publicKey", + "index": false + }, + { + "name": "annualRewardsRate", + "type": "u64", + "index": false + }, + { + "name": "rewardsShare", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 300, + "name": "Unauthorized", + "msg": "You are not authorized to perform this action." + }, + { + "code": 301, + "name": "InsufficientBalance", + "msg": "Insufficient staked balance for withdraw request." + }, + { + "code": 302, + "name": "PendingAuthorityNotSet", + "msg": "Pending authority not set" + }, + { + "code": 303, + "name": "InvalidRewardsShare", + "msg": "Invalid quarry rewards share" + }, + { + "code": 304, + "name": "InsufficientAllowance", + "msg": "Insufficient allowance." + }, + { + "code": 305, + "name": "NewVaultNotEmpty", + "msg": "New vault not empty." + }, + { + "code": 306, + "name": "NotEnoughTokens", + "msg": "Not enough tokens." + }, + { + "code": 307, + "name": "InvalidTimestamp", + "msg": "Invalid timestamp." + }, + { + "code": 308, + "name": "InvalidMaxClaimFee", + "msg": "Invalid max claim fee." + }, + { + "code": 309, + "name": "MaxAnnualRewardsRateExceeded", + "msg": "Max annual rewards rate exceeded." + }, + { + "code": 310, + "name": "Paused", + "msg": "Rewarder is paused." + }, + { + "code": 311, + "name": "UpperboundExceeded", + "msg": "Rewards earned exceeded quarry's upper bound." + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/sol_cerberus.json b/basics/anchorpy-main/tests/idls/sol_cerberus.json new file mode 100644 index 0000000..5c4cbdc --- /dev/null +++ b/basics/anchorpy-main/tests/idls/sol_cerberus.json @@ -0,0 +1,1464 @@ +{ + "version": "0.1.7", + "name": "sol_cerberus", + "instructions": [ + { + "name": "initializeApp", + "accounts": [ + { + "name": "authority", + "isMut": true, + "isSigner": true + }, + { + "name": "app", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "arg", + "type": { + "defined": "AppData" + }, + "path": "app_data.id" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "appData", + "type": { + "defined": "AppData" + } + } + ] + }, + { + "name": "updateApp", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "app", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "app.id" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "appData", + "type": { + "defined": "UpdateAppData" + } + } + ] + }, + { + "name": "deleteApp", + "accounts": [ + { + "name": "authority", + "isMut": true, + "isSigner": true + }, + { + "name": "app", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "app.id" + } + ] + } + }, + { + "name": "collector", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "addRule", + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "rule", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "arg", + "type": { + "defined": "RuleData" + }, + "path": "rule_data.namespace" + }, + { + "kind": "arg", + "type": { + "defined": "RuleData" + }, + "path": "rule_data.role" + }, + { + "kind": "arg", + "type": { + "defined": "RuleData" + }, + "path": "rule_data.resource" + }, + { + "kind": "arg", + "type": { + "defined": "RuleData" + }, + "path": "rule_data.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusApp", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusRole", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusRule", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule.app_id" + } + ] + } + }, + { + "name": "solCerberusRule2", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule2.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule2.app_id" + } + ] + } + }, + { + "name": "solCerberusToken", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusMetadata", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusSeed", + "isMut": true, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "seed" + }, + { + "kind": "account", + "type": "publicKey", + "path": "signer" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "ruleData", + "type": { + "defined": "RuleData" + } + } + ] + }, + { + "name": "deleteRule", + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "rule", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusApp", + "isMut": false, + "isSigner": false + }, + { + "name": "solCerberusRole", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusRule", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule.app_id" + } + ] + } + }, + { + "name": "solCerberusRule2", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule2.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule2.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule2.app_id" + } + ] + } + }, + { + "name": "solCerberusToken", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusMetadata", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusSeed", + "isMut": true, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "seed" + }, + { + "kind": "account", + "type": "publicKey", + "path": "signer" + } + ] + } + }, + { + "name": "collector", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "assignRole", + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "role", + "isMut": true, + "isSigner": false + }, + { + "name": "solCerberusApp", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusRole", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusRule", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule.app_id" + } + ] + } + }, + { + "name": "solCerberusToken", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusMetadata", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusSeed", + "isMut": true, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "seed" + }, + { + "kind": "account", + "type": "publicKey", + "path": "signer" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "assignRoleData", + "type": { + "defined": "AssignRoleData" + } + } + ] + }, + { + "name": "deleteAssignedRole", + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "role", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "string", + "account": "Role", + "path": "role.role" + }, + { + "kind": "account", + "type": { + "option": "publicKey" + }, + "account": "Role", + "path": "role.address" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusApp", + "isMut": false, + "isSigner": false + }, + { + "name": "solCerberusRole", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusRule", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule.app_id" + } + ] + } + }, + { + "name": "solCerberusToken", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusMetadata", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusSeed", + "isMut": true, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "seed" + }, + { + "kind": "account", + "type": "publicKey", + "path": "signer" + } + ] + } + }, + { + "name": "collector", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "updateCache", + "docs": [ + "* Updates either app.roles_updated_at or app.rules_updated_at fields, so clients\n * can keep track and cache roles & rules accordingly." + ], + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "app", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "app.id" + } + ] + }, + "relations": [ + "authority" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "cacheUpdated", + "type": "u8" + } + ] + }, + { + "name": "allowed", + "docs": [ + "* Checks if the current user is authorized to run the instruction,\n * throwing \"Unauthorized\" error otherwise." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "solCerberusApp", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "app" + }, + { + "kind": "account", + "type": "publicKey", + "account": "App", + "path": "sol_cerberus_app.id" + } + ] + } + }, + { + "name": "solCerberusRule", + "isMut": false, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "u8", + "account": "Rule", + "path": "sol_cerberus_rule.namespace" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.role" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.resource" + }, + { + "kind": "account", + "type": "string", + "account": "Rule", + "path": "sol_cerberus_rule.permission" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Rule", + "path": "sol_cerberus_rule.app_id" + } + ] + } + }, + { + "name": "solCerberusRole", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusToken", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusMetadata", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "solCerberusSeed", + "isMut": true, + "isSigner": false, + "isOptional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "seed" + }, + { + "kind": "account", + "type": "publicKey", + "path": "signer" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "allowedRule", + "type": { + "defined": "AllowedRule" + } + } + ] + } + ], + "accounts": [ + { + "name": "App", + "type": { + "kind": "struct", + "fields": [ + { + "name": "id", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "recovery", + "type": { + "option": "publicKey" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "rolesUpdatedAt", + "type": "i64" + }, + { + "name": "rulesUpdatedAt", + "type": "i64" + }, + { + "name": "cached", + "type": "bool" + }, + { + "name": "fee", + "type": { + "option": "u64" + } + }, + { + "name": "class", + "type": "u8" + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + } + ] + } + }, + { + "name": "Seed", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "type": "bool" + } + ] + } + }, + { + "name": "Role", + "type": { + "kind": "struct", + "fields": [ + { + "name": "appId", + "type": "publicKey" + }, + { + "name": "address", + "type": { + "option": "publicKey" + } + }, + { + "name": "role", + "type": "string" + }, + { + "name": "addressType", + "type": { + "defined": "AddressType" + } + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + }, + { + "name": "bump", + "type": "u8" + } + ] + } + }, + { + "name": "Rule", + "type": { + "kind": "struct", + "fields": [ + { + "name": "appId", + "type": "publicKey" + }, + { + "name": "namespace", + "type": "u8" + }, + { + "name": "role", + "type": "string" + }, + { + "name": "resource", + "type": "string" + }, + { + "name": "permission", + "type": "string" + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + }, + { + "name": "bump", + "type": "u8" + } + ] + } + } + ], + "types": [ + { + "name": "AllowedRule", + "type": { + "kind": "struct", + "fields": [ + { + "name": "appId", + "type": "publicKey" + }, + { + "name": "namespace", + "type": "u8" + }, + { + "name": "resource", + "type": "string" + }, + { + "name": "permission", + "type": "string" + } + ] + } + }, + { + "name": "AppData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "id", + "type": "publicKey" + }, + { + "name": "recovery", + "type": { + "option": "publicKey" + } + }, + { + "name": "name", + "type": "string" + }, + { + "name": "cached", + "type": "bool" + } + ] + } + }, + { + "name": "UpdateAppData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "recovery", + "type": { + "option": "publicKey" + } + }, + { + "name": "name", + "type": "string" + }, + { + "name": "cached", + "type": "bool" + }, + { + "name": "fee", + "type": { + "option": "u64" + } + }, + { + "name": "class", + "type": "u8" + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + } + ] + } + }, + { + "name": "AssignRoleData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": { + "option": "publicKey" + } + }, + { + "name": "role", + "type": "string" + }, + { + "name": "addressType", + "type": { + "defined": "AddressType" + } + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + } + ] + } + }, + { + "name": "RuleData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "namespace", + "type": "u8" + }, + { + "name": "role", + "type": "string" + }, + { + "name": "resource", + "type": "string" + }, + { + "name": "permission", + "type": "string" + }, + { + "name": "expiresAt", + "type": { + "option": "i64" + } + } + ] + } + }, + { + "name": "Classes", + "docs": [ + "Classes:", + "0 => Trial (Apps with default fees)", + "1 => Free (Apps with no fees)" + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Trial" + }, + { + "name": "Free" + } + ] + } + }, + { + "name": "CacheUpdated", + "docs": [ + "CacheUpdated:", + "0 => Roles (When roles change)", + "1 => Rules (When rules change)" + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Roles" + }, + { + "name": "Rules" + } + ] + } + }, + { + "name": "AddressType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Wallet" + }, + { + "name": "Nft" + }, + { + "name": "Collection" + } + ] + } + }, + { + "name": "Namespaces", + "docs": [ + "Namespaces:", + "0 => Rule (Normal permissions)", + "1 => AssignRole (White list of roles that can be assigned by certain role)", + "2 => DeleteAssignRole (White list of roles that can be deleted by certain role)", + "3 => AddRuleNSRole (White list of namespaces and roles that can be created by certain role)", + "4 => AddRuleResourcePerm (White list of resources and permissions that can be created by certain role)", + "5 => DeleteRuleNSRole (White list of namespaces and roles that can be deleted by certain role)", + "6 => DeleteRuleResourcePerm (White list of resources and permissions that can be deleted by certain role)" + ], + "type": { + "kind": "enum", + "variants": [ + { + "name": "Rule" + }, + { + "name": "AssignRole" + }, + { + "name": "DeleteAssignRole" + }, + { + "name": "AddRuleNSRole" + }, + { + "name": "AddRuleResourcePerm" + }, + { + "name": "DeleteRuleNSRole" + }, + { + "name": "DeleteRuleResourcePerm" + } + ] + } + } + ], + "events": [ + { + "name": "AppChanged", + "fields": [ + { + "name": "time", + "type": "i64", + "index": false + }, + { + "name": "appId", + "type": "publicKey", + "index": true + }, + { + "name": "authority", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "RolesChanged", + "fields": [ + { + "name": "time", + "type": "i64", + "index": false + }, + { + "name": "appId", + "type": "publicKey", + "index": true + } + ] + }, + { + "name": "RulesChanged", + "fields": [ + { + "name": "time", + "type": "i64", + "index": false + }, + { + "name": "appId", + "type": "publicKey", + "index": true + } + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "UnauthorizedAuthorityUpdate", + "msg": "Only current Authority or Recovery accounts can update the App authority" + }, + { + "code": 6001, + "name": "InvalidRule", + "msg": "Role, Resource or Permission must be betwen 1 and 16 alphanumeric characters long" + }, + { + "code": 6002, + "name": "InvalidRole", + "msg": "Role must be between 1 and 16 alphanumeric characters long" + }, + { + "code": 6003, + "name": "StringTooShort", + "msg": "The provided string is too short" + }, + { + "code": 6004, + "name": "StringTooLong", + "msg": "The provided string is too long" + }, + { + "code": 6005, + "name": "Unauthorized", + "msg": "The user does not have enough privileges to perform this action" + }, + { + "code": 6006, + "name": "InvalidAppID", + "msg": "The Sol Cerberus APP ID does not match the one defined in the program" + }, + { + "code": 6007, + "name": "InvalidAddressType", + "msg": "Invalid address type, mus be either 'Wallet', 'Nft', 'Collection' or a wildcard '*'" + }, + { + "code": 6008, + "name": "InvalidNamespace", + "msg": "Invalid namespace, must be either an u8 number (0-255) or a wildcard '*'" + }, + { + "code": 6009, + "name": "MissingSolCerberusAppId", + "msg": "SOL_CERBERUS_APP_ID is missing on lib.rs" + }, + { + "code": 6010, + "name": "MissingSeedAccount", + "msg": "The Sol Cerberus Seed account is missing" + }, + { + "code": 6011, + "name": "UnauthorizedProgramAuthority", + "msg": "Only program authority can perform this action" + }, + { + "code": 6012, + "name": "InsufficientFunds", + "msg": "Insufficient funds for transaction" + } + ], + "metadata": { + "address": "SCERbrcgSPwgkrJ7j4TABr17dhYzdgiwPZUSSfFPt8x" + } +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/spl_token.json b/basics/anchorpy-main/tests/idls/spl_token.json new file mode 100644 index 0000000..773e470 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/spl_token.json @@ -0,0 +1,844 @@ +{ + "version": "3.3.0", + "name": "spl_token", + "instructions": [ + { + "name": "initializeMint", + "accounts": [ + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "decimals", + "type": "u8" + }, + { + "name": "mintAuthority", + "type": "publicKey" + }, + { + "name": "freezeAuthority", + "type": { + "defined": "COption" + } + } + ] + }, + { + "name": "initializeAccount", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "initializeMultisig", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "m", + "type": "u8" + } + ] + }, + { + "name": "transfer", + "accounts": [ + { + "name": "source", + "isMut": true, + "isSigner": false + }, + { + "name": "destination", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "approve", + "accounts": [ + { + "name": "source", + "isMut": true, + "isSigner": false + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "revoke", + "accounts": [ + { + "name": "source", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "setAuthority", + "accounts": [ + { + "name": "owned", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "signer", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "authorityType", + "type": { + "defined": "AuthorityType" + } + }, + { + "name": "newAuthority", + "type": { + "defined": "COption" + } + } + ] + }, + { + "name": "mintTo", + "accounts": [ + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "burn", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "closeAccount", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "destination", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "freezeAccount", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "thawAccount", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "transferChecked", + "accounts": [ + { + "name": "source", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "destination", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + } + ] + }, + { + "name": "approveChecked", + "accounts": [ + { + "name": "source", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + } + ] + }, + { + "name": "mintToChecked", + "accounts": [ + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + } + ] + }, + { + "name": "burnChecked", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + } + ] + }, + { + "name": "initializeAccount2", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "syncNative", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "initializeAccount3", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "owner", + "type": "publicKey" + } + ] + }, + { + "name": "initializeMultisig2", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "signer", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "m", + "type": "u8" + } + ] + }, + { + "name": "initializeMint2", + "accounts": [ + { + "name": "mint", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "decimals", + "type": "u8" + }, + { + "name": "mintAuthority", + "type": "publicKey" + }, + { + "name": "freezeAuthority", + "type": { + "defined": "COption" + } + } + ] + }, + { + "name": "getAccountDataSize", + "accounts": [ + { + "name": "mint", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "initializeImmutableOwner", + "accounts": [ + { + "name": "account", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "amountToUiAmount", + "accounts": [ + { + "name": "mint", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "uiAmountToAmount", + "accounts": [ + { + "name": "mint", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "uiAmount", + "type": { + "defined": "&'astr" + } + } + ] + } + ], + "accounts": [ + { + "name": "Mint", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mintAuthority", + "type": { + "defined": "COption" + } + }, + { + "name": "supply", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + }, + { + "name": "isInitialized", + "type": "bool" + }, + { + "name": "freezeAuthority", + "type": { + "defined": "COption" + } + } + ] + } + }, + { + "name": "Account", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "delegate", + "type": { + "defined": "COption" + } + }, + { + "name": "state", + "type": { + "defined": "AccountState" + } + }, + { + "name": "isNative", + "type": { + "defined": "COption" + } + }, + { + "name": "delegatedAmount", + "type": "u64" + }, + { + "name": "closeAuthority", + "type": { + "defined": "COption" + } + } + ] + } + }, + { + "name": "Multisig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "m", + "type": "u8" + }, + { + "name": "n", + "type": "u8" + }, + { + "name": "isInitialized", + "type": "bool" + }, + { + "name": "signers", + "type": { + "array": [ + "publicKey", + 11 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "AccountState", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Uninitialized" + }, + { + "name": "Initialized" + }, + { + "name": "Frozen" + } + ] + } + }, + { + "name": "AuthorityType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "MintTokens" + }, + { + "name": "FreezeAccount" + }, + { + "name": "AccountOwner" + }, + { + "name": "CloseAccount" + } + ] + } + } + ], + "errors": [ + { + "code": 0, + "name": "NotRentExempt", + "msg": "Lamport balance below rent-exempt threshold" + }, + { + "code": 1, + "name": "InsufficientFunds", + "msg": "Insufficient funds" + }, + { + "code": 2, + "name": "InvalidMint", + "msg": "Invalid Mint" + }, + { + "code": 3, + "name": "MintMismatch", + "msg": "Account not associated with this Mint" + }, + { + "code": 4, + "name": "OwnerMismatch", + "msg": "Owner does not match" + }, + { + "code": 5, + "name": "FixedSupply", + "msg": "Fixed supply" + }, + { + "code": 6, + "name": "AlreadyInUse", + "msg": "Already in use" + }, + { + "code": 7, + "name": "InvalidNumberOfProvidedSigners", + "msg": "Invalid number of provided signers" + }, + { + "code": 8, + "name": "InvalidNumberOfRequiredSigners", + "msg": "Invalid number of required signers" + }, + { + "code": 9, + "name": "UninitializedState", + "msg": "State is unititialized" + }, + { + "code": 10, + "name": "NativeNotSupported", + "msg": "Instruction does not support native tokens" + }, + { + "code": 11, + "name": "NonNativeHasBalance", + "msg": "Non-native account can only be closed if its balance is zero" + }, + { + "code": 12, + "name": "InvalidInstruction", + "msg": "Invalid instruction" + }, + { + "code": 13, + "name": "InvalidState", + "msg": "State is invalid for requested operation" + }, + { + "code": 14, + "name": "Overflow", + "msg": "Operation overflowed" + }, + { + "code": 15, + "name": "AuthorityTypeNotSupported", + "msg": "Account does not support specified authority type" + }, + { + "code": 16, + "name": "MintCannotFreeze", + "msg": "This token mint cannot freeze accounts" + }, + { + "code": 17, + "name": "AccountFrozen", + "msg": "Account is frozen" + }, + { + "code": 18, + "name": "MintDecimalsMismatch", + "msg": "The provided decimals value different from the Mint decimals" + }, + { + "code": 19, + "name": "NonNativeNotSupported", + "msg": "Instruction does not support non-native tokens" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/swap.json b/basics/anchorpy-main/tests/idls/swap.json new file mode 100644 index 0000000..6ea31d0 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/swap.json @@ -0,0 +1,352 @@ +{ + "version": "0.0.0", + "name": "swap", + "instructions": [ + { + "name": "swap", + "accounts": [ + { + "name": "market", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "openOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "requestQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "eventQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "bids", + "isMut": true, + "isSigner": false + }, + { + "name": "asks", + "isMut": true, + "isSigner": false + }, + { + "name": "orderPayerTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "coinVault", + "isMut": true, + "isSigner": false + }, + { + "name": "pcVault", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "coinWallet", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "pcWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "dexProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "side", + "type": { + "defined": "Side" + } + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "minExpectedSwapAmount", + "type": "u64" + } + ] + }, + { + "name": "swapTransitive", + "accounts": [ + { + "name": "from", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "openOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "requestQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "eventQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "bids", + "isMut": true, + "isSigner": false + }, + { + "name": "asks", + "isMut": true, + "isSigner": false + }, + { + "name": "orderPayerTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "coinVault", + "isMut": true, + "isSigner": false + }, + { + "name": "pcVault", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "coinWallet", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "to", + "accounts": [ + { + "name": "market", + "isMut": true, + "isSigner": false + }, + { + "name": "openOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "requestQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "eventQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "bids", + "isMut": true, + "isSigner": false + }, + { + "name": "asks", + "isMut": true, + "isSigner": false + }, + { + "name": "orderPayerTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "coinVault", + "isMut": true, + "isSigner": false + }, + { + "name": "pcVault", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "coinWallet", + "isMut": true, + "isSigner": false + } + ] + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "pcWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "dexProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "minExpectedSwapAmount", + "type": "u64" + } + ] + } + ], + "types": [ + { + "name": "Side", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Bid" + }, + { + "name": "Ask" + } + ] + } + } + ], + "events": [ + { + "name": "DidSwap", + "fields": [ + { + "name": "givenAmount", + "type": "u64", + "index": false + }, + { + "name": "minExpectedSwapAmount", + "type": "u64", + "index": false + }, + { + "name": "fromAmount", + "type": "u64", + "index": false + }, + { + "name": "toAmount", + "type": "u64", + "index": false + }, + { + "name": "spillAmount", + "type": "u64", + "index": false + }, + { + "name": "fromMint", + "type": "publicKey", + "index": false + }, + { + "name": "toMint", + "type": "publicKey", + "index": false + }, + { + "name": "quoteMint", + "type": "publicKey", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 300, + "name": "SwapTokensCannotMatch", + "msg": "The tokens being swapped must have different mints" + }, + { + "code": 301, + "name": "SlippageExceeded", + "msg": "Slippage tolerance exceeded" + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/switchboard.json b/basics/anchorpy-main/tests/idls/switchboard.json new file mode 100644 index 0000000..c476c4a --- /dev/null +++ b/basics/anchorpy-main/tests/idls/switchboard.json @@ -0,0 +1 @@ +{"version": "0.1.0", "name": "switchboard_v2", "instructions": [{"name": "vaultTransfer", "accounts": [{"name": "state", "isMut": false, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "to", "isMut": true, "isSigner": false}, {"name": "vault", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "VaultTransferParams"}}]}, {"name": "programInit", "accounts": [{"name": "state", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": false}, {"name": "tokenMint", "isMut": true, "isSigner": false}, {"name": "vault", "isMut": true, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": false}, {"name": "systemProgram", "isMut": false, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "ProgramInitParams"}}]}, {"name": "programConfig", "accounts": [{"name": "authority", "isMut": false, "isSigner": true}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "ProgramConfigParams"}}]}, {"name": "aggregatorInit", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": false}, {"name": "queue", "isMut": false, "isSigner": false}, {"name": "authorWallet", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorInitParams"}}]}, {"name": "aggregatorLock", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": true, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "AggregatorLockParams"}}]}, {"name": "aggregatorSetAuthority", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "newAuthority", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorSetAuthorityParams"}}]}, {"name": "aggregatorSetBatchSize", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "AggregatorSetBatchSizeParams"}}]}, {"name": "aggregatorSetMinJobs", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "AggregatorSetMinJobsParams"}}]}, {"name": "aggregatorSetMinOracles", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "AggregatorSetMinOraclesParams"}}]}, {"name": "aggregatorSetVarianceThreshold", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "AggregatorSetVarianceThresholdParams"}}]}, {"name": "aggregatorSetHistoryBuffer", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "buffer", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorSetHistoryBufferParams"}}]}, {"name": "aggregatorSetQueue", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "queue", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorSetQueueParams"}}]}, {"name": "aggregatorAddJob", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "job", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorAddJobParams"}}]}, {"name": "aggregatorRemoveJob", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}, {"name": "job", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorRemoveJobParams"}}]}, {"name": "aggregatorOpenRound", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "lease", "isMut": true, "isSigner": false}, {"name": "oracleQueue", "isMut": true, "isSigner": false}, {"name": "queueAuthority", "isMut": false, "isSigner": false}, {"name": "permission", "isMut": true, "isSigner": false}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "payoutWallet", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "dataBuffer", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorOpenRoundParams"}}]}, {"name": "aggregatorSaveResult", "accounts": [{"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "oracle", "isMut": true, "isSigner": false}, {"name": "oracleAuthority", "isMut": false, "isSigner": true}, {"name": "oracleQueue", "isMut": false, "isSigner": false}, {"name": "queueAuthority", "isMut": false, "isSigner": false}, {"name": "feedPermission", "isMut": true, "isSigner": false}, {"name": "oraclePermission", "isMut": false, "isSigner": false}, {"name": "lease", "isMut": true, "isSigner": false}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "historyBuffer", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "AggregatorSaveResultParams"}}]}, {"name": "jobInit", "accounts": [{"name": "job", "isMut": true, "isSigner": false}, {"name": "authorWallet", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "JobInitParams"}}]}, {"name": "permissionInit", "accounts": [{"name": "permission", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": false}, {"name": "granter", "isMut": false, "isSigner": false}, {"name": "grantee", "isMut": false, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": true}, {"name": "systemProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "PermissionInitParams"}}]}, {"name": "permissionSet", "accounts": [{"name": "permission", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "PermissionSetParams"}}]}, {"name": "oracleQueueInit", "accounts": [{"name": "oracleQueue", "isMut": true, "isSigner": true}, {"name": "authority", "isMut": false, "isSigner": false}, {"name": "buffer", "isMut": true, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": false}, {"name": "systemProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "OracleQueueInitParams"}}]}, {"name": "oracleQueueSetRewards", "accounts": [{"name": "queue", "isMut": true, "isSigner": false}, {"name": "authority", "isMut": false, "isSigner": true}], "args": [{"name": "params", "type": {"defined": "OracleQueueSetRewardsParams"}}]}, {"name": "oracleInit", "accounts": [{"name": "oracle", "isMut": true, "isSigner": false}, {"name": "oracleAuthority", "isMut": false, "isSigner": false}, {"name": "wallet", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "queue", "isMut": false, "isSigner": false}, {"name": "payer", "isMut": false, "isSigner": false}, {"name": "systemProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "OracleInitParams"}}]}, {"name": "oracleHeartbeat", "accounts": [{"name": "oracle", "isMut": true, "isSigner": false}, {"name": "oracleAuthority", "isMut": false, "isSigner": true}, {"name": "tokenAccount", "isMut": false, "isSigner": false}, {"name": "gcOracle", "isMut": true, "isSigner": false}, {"name": "oracleQueue", "isMut": true, "isSigner": false}, {"name": "permission", "isMut": false, "isSigner": false}, {"name": "dataBuffer", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "OracleHeartbeatParams"}}]}, {"name": "oracleWithdraw", "accounts": [{"name": "oracle", "isMut": true, "isSigner": false}, {"name": "oracleAuthority", "isMut": false, "isSigner": true}, {"name": "tokenAccount", "isMut": true, "isSigner": false}, {"name": "withdrawAccount", "isMut": true, "isSigner": false}, {"name": "oracleQueue", "isMut": true, "isSigner": false}, {"name": "permission", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": true}, {"name": "systemProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "OracleWithdrawParams"}}]}, {"name": "leaseInit", "accounts": [{"name": "lease", "isMut": true, "isSigner": false}, {"name": "queue", "isMut": true, "isSigner": false}, {"name": "aggregator", "isMut": false, "isSigner": false}, {"name": "funder", "isMut": true, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": true}, {"name": "systemProgram", "isMut": false, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "owner", "isMut": true, "isSigner": true}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "LeaseInitParams"}}]}, {"name": "leaseExtend", "accounts": [{"name": "lease", "isMut": true, "isSigner": false}, {"name": "aggregator", "isMut": false, "isSigner": false}, {"name": "queue", "isMut": false, "isSigner": false}, {"name": "funder", "isMut": true, "isSigner": false}, {"name": "owner", "isMut": true, "isSigner": true}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "LeaseExtendParams"}}]}, {"name": "leaseWithdraw", "accounts": [{"name": "lease", "isMut": true, "isSigner": false}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "aggregator", "isMut": false, "isSigner": false}, {"name": "queue", "isMut": false, "isSigner": false}, {"name": "withdrawAuthority", "isMut": false, "isSigner": true}, {"name": "withdrawAccount", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "LeaseWithdrawParams"}}]}, {"name": "crankInit", "accounts": [{"name": "crank", "isMut": true, "isSigner": true}, {"name": "queue", "isMut": false, "isSigner": false}, {"name": "buffer", "isMut": true, "isSigner": false}, {"name": "payer", "isMut": true, "isSigner": false}, {"name": "systemProgram", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "CrankInitParams"}}]}, {"name": "crankPush", "accounts": [{"name": "crank", "isMut": true, "isSigner": false}, {"name": "aggregator", "isMut": true, "isSigner": false}, {"name": "oracleQueue", "isMut": true, "isSigner": false}, {"name": "queueAuthority", "isMut": false, "isSigner": false}, {"name": "permission", "isMut": false, "isSigner": false}, {"name": "lease", "isMut": true, "isSigner": false}, {"name": "escrow", "isMut": true, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "dataBuffer", "isMut": true, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "CrankPushParams"}}]}, {"name": "crankPop", "accounts": [{"name": "crank", "isMut": true, "isSigner": false}, {"name": "oracleQueue", "isMut": true, "isSigner": false}, {"name": "queueAuthority", "isMut": false, "isSigner": false}, {"name": "programState", "isMut": false, "isSigner": false}, {"name": "payoutWallet", "isMut": true, "isSigner": false}, {"name": "tokenProgram", "isMut": false, "isSigner": false}, {"name": "crankDataBuffer", "isMut": true, "isSigner": false}, {"name": "queueDataBuffer", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "CrankPopParams"}}]}, {"name": "ecvrfVerify", "accounts": [{"name": "randomnessProducer", "isMut": false, "isSigner": false}], "args": [{"name": "params", "type": {"defined": "EcvrfVerifyParams"}}]}], "accounts": [{"name": "SbState", "type": {"kind": "struct", "fields": [{"name": "authority", "type": "publicKey"}, {"name": "tokenMint", "type": "publicKey"}, {"name": "tokenVault", "type": "publicKey"}, {"name": "ebuf", "type": {"array": ["u8", 1024]}}]}}, {"name": "AggregatorAccountData", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 128]}}, {"name": "authorWallet", "type": "publicKey"}, {"name": "queuePubkey", "type": "publicKey"}, {"name": "oracleRequestBatchSize", "type": "u32"}, {"name": "minOracleResults", "type": "u32"}, {"name": "minJobResults", "type": "u32"}, {"name": "minUpdateDelaySeconds", "type": "u32"}, {"name": "startAfter", "type": "i64"}, {"name": "varianceThreshold", "type": {"defined": "SwitchboardDecimal"}}, {"name": "forceReportPeriod", "type": "i64"}, {"name": "expiration", "type": "i64"}, {"name": "consecutiveFailureCount", "type": "u64"}, {"name": "nextAllowedUpdateTime", "type": "i64"}, {"name": "isLocked", "type": "bool"}, {"name": "crankPubkey", "type": "publicKey"}, {"name": "latestConfirmedRound", "type": {"defined": "AggregatorRound"}}, {"name": "currentRound", "type": {"defined": "AggregatorRound"}}, {"name": "jobPubkeysData", "type": {"array": ["publicKey", 16]}}, {"name": "jobHashes", "type": {"array": [{"defined": "Hash"}, 16]}}, {"name": "jobPubkeysSize", "type": "u32"}, {"name": "jobsChecksum", "type": {"array": ["u8", 32]}}, {"name": "authority", "type": "publicKey"}, {"name": "historyBuffer", "type": "publicKey"}, {"name": "ebuf", "type": {"array": ["u8", 192]}}]}}, {"name": "PermissionAccountData", "type": {"kind": "struct", "fields": [{"name": "authority", "type": "publicKey"}, {"name": "permissions", "type": "u32"}, {"name": "granter", "type": "publicKey"}, {"name": "grantee", "type": "publicKey"}, {"name": "expiration", "type": "i64"}, {"name": "ebuf", "type": {"array": ["u8", 256]}}]}}, {"name": "LeaseAccountData", "type": {"kind": "struct", "fields": [{"name": "escrow", "type": "publicKey"}, {"name": "queue", "type": "publicKey"}, {"name": "aggregator", "type": "publicKey"}, {"name": "tokenProgram", "type": "publicKey"}, {"name": "isActive", "type": "bool"}, {"name": "crankRowCount", "type": "u32"}, {"name": "createdAt", "type": "i64"}, {"name": "updateCount", "type": "u128"}, {"name": "withdrawAuthority", "type": "publicKey"}, {"name": "ebuf", "type": {"array": ["u8", 256]}}]}}, {"name": "OracleQueueAccountData", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 64]}}, {"name": "authority", "type": "publicKey"}, {"name": "oracleTimeout", "type": "u32"}, {"name": "reward", "type": "u64"}, {"name": "minStake", "type": "u64"}, {"name": "slashingEnabled", "type": "bool"}, {"name": "varianceToleranceMultiplier", "type": {"defined": "SwitchboardDecimal"}}, {"name": "feedProbationPeriod", "type": "u32"}, {"name": "currIdx", "type": "u32"}, {"name": "size", "type": "u32"}, {"name": "gcIdx", "type": "u32"}, {"name": "consecutiveFeedFailureLimit", "type": "u64"}, {"name": "consecutiveOracleFailureLimit", "type": "u64"}, {"name": "unpermissionedFeedsEnabled", "type": "bool"}, {"name": "ebuf", "type": {"array": ["u8", 1023]}}, {"name": "maxSize", "type": "u32"}, {"name": "dataBuffer", "type": "publicKey"}]}}, {"name": "CrankAccountData", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 64]}}, {"name": "queuePubkey", "type": "publicKey"}, {"name": "pqSize", "type": "u32"}, {"name": "maxRows", "type": "u32"}, {"name": "jitterModifier", "type": "u8"}, {"name": "ebuf", "type": {"array": ["u8", 255]}}, {"name": "dataBuffer", "type": "publicKey"}]}}, {"name": "VrfAccountData", "type": {"kind": "struct", "fields": [{"name": "counter", "type": "u128"}, {"name": "latestFinalizedRound", "type": {"defined": "VrfRound"}}, {"name": "currentRound", "type": {"defined": "VrfRound"}}, {"name": "ebuf", "type": {"array": ["u8", 255]}}]}}, {"name": "OracleAccountData", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 128]}}, {"name": "oracleAuthority", "type": "publicKey"}, {"name": "lastHeartbeat", "type": "i64"}, {"name": "numInUse", "type": "u32"}, {"name": "tokenAccount", "type": "publicKey"}, {"name": "queuePubkey", "type": "publicKey"}, {"name": "metrics", "type": {"defined": "OracleMetrics"}}, {"name": "ebuf", "type": {"array": ["u8", 256]}}]}}, {"name": "JobAccountData", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 64]}}, {"name": "authorWallet", "type": "publicKey"}, {"name": "expiration", "type": "i64"}, {"name": "hash", "type": {"array": ["u8", 32]}}, {"name": "data", "type": "bytes"}, {"name": "referenceCount", "type": "u32"}]}}], "types": [{"name": "AggregatorAddJobParams", "type": {"kind": "struct", "fields": []}}, {"name": "AggregatorInitParams", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 128]}}, {"name": "batchSize", "type": "u32"}, {"name": "minOracleResults", "type": "u32"}, {"name": "minJobResults", "type": "u32"}, {"name": "minUpdateDelaySeconds", "type": "u32"}, {"name": "startAfter", "type": "i64"}, {"name": "varianceThreshold", "type": {"defined": "BorshDecimal"}}, {"name": "forceReportPeriod", "type": "i64"}, {"name": "expiration", "type": "i64"}, {"name": "stateBump", "type": "u8"}]}}, {"name": "AggregatorLockParams", "type": {"kind": "struct", "fields": []}}, {"name": "AggregatorOpenRoundParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "leaseBump", "type": "u8"}, {"name": "permissionBump", "type": "u8"}, {"name": "jitter", "type": "u8"}]}}, {"name": "AggregatorRemoveJobParams", "type": {"kind": "struct", "fields": [{"name": "jobIdx", "type": "u32"}]}}, {"name": "AggregatorSaveResultParams", "type": {"kind": "struct", "fields": [{"name": "oracleIdx", "type": "u32"}, {"name": "error", "type": "bool"}, {"name": "value", "type": {"defined": "BorshDecimal"}}, {"name": "jobsChecksum", "type": {"array": ["u8", 32]}}, {"name": "minResponse", "type": {"defined": "BorshDecimal"}}, {"name": "maxResponse", "type": {"defined": "BorshDecimal"}}, {"name": "feedPermissionBump", "type": "u8"}, {"name": "oraclePermissionBump", "type": "u8"}, {"name": "leaseBump", "type": "u8"}, {"name": "stateBump", "type": "u8"}]}}, {"name": "AggregatorSetAuthorityParams", "type": {"kind": "struct", "fields": []}}, {"name": "AggregatorSetBatchSizeParams", "type": {"kind": "struct", "fields": [{"name": "batchSize", "type": "u32"}]}}, {"name": "AggregatorSetHistoryBufferParams", "type": {"kind": "struct", "fields": []}}, {"name": "AggregatorSetMinJobsParams", "type": {"kind": "struct", "fields": [{"name": "minJobResults", "type": "u32"}]}}, {"name": "AggregatorSetMinOraclesParams", "type": {"kind": "struct", "fields": [{"name": "minOracleResults", "type": "u32"}]}}, {"name": "AggregatorSetQueueParams", "type": {"kind": "struct", "fields": []}}, {"name": "AggregatorSetVarianceThresholdParams", "type": {"kind": "struct", "fields": [{"name": "varianceThreshold", "type": {"defined": "BorshDecimal"}}]}}, {"name": "CrankInitParams", "type": {"kind": "struct", "fields": [{"name": "name", "type": "bytes"}, {"name": "metadata", "type": "bytes"}, {"name": "crankSize", "type": "u32"}]}}, {"name": "CrankPopParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "leaseBumps", "type": "bytes"}, {"name": "permissionBumps", "type": "bytes"}, {"name": "nonce", "type": {"option": "u32"}}, {"name": "failOpenOnAccountMismatch", "type": {"option": "bool"}}]}}, {"name": "CrankPushParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "permissionBump", "type": "u8"}]}}, {"name": "EcvrfVerifyParams", "type": {"kind": "struct", "fields": [{"name": "proof", "type": "bytes"}, {"name": "alpha", "type": "bytes"}]}}, {"name": "JobInitParams", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "expiration", "type": "i64"}, {"name": "stateBump", "type": "u8"}, {"name": "data", "type": "bytes"}]}}, {"name": "LeaseExtendParams", "type": {"kind": "struct", "fields": [{"name": "loadAmount", "type": "u64"}, {"name": "leaseBump", "type": "u8"}, {"name": "stateBump", "type": "u8"}]}}, {"name": "LeaseInitParams", "type": {"kind": "struct", "fields": [{"name": "loadAmount", "type": "u64"}, {"name": "withdrawAuthority", "type": "publicKey"}, {"name": "leaseBump", "type": "u8"}, {"name": "stateBump", "type": "u8"}]}}, {"name": "LeaseWithdrawParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "leaseBump", "type": "u8"}, {"name": "amount", "type": "u64"}]}}, {"name": "OracleHeartbeatParams", "type": {"kind": "struct", "fields": [{"name": "permissionBump", "type": "u8"}]}}, {"name": "OracleInitParams", "type": {"kind": "struct", "fields": [{"name": "name", "type": "bytes"}, {"name": "metadata", "type": "bytes"}, {"name": "stateBump", "type": "u8"}, {"name": "oracleBump", "type": "u8"}]}}, {"name": "OracleQueueInitParams", "type": {"kind": "struct", "fields": [{"name": "name", "type": {"array": ["u8", 32]}}, {"name": "metadata", "type": {"array": ["u8", 64]}}, {"name": "reward", "type": "u64"}, {"name": "minStake", "type": "u64"}, {"name": "feedProbationPeriod", "type": "u32"}, {"name": "oracleTimeout", "type": "u32"}, {"name": "slashingEnabled", "type": "bool"}, {"name": "varianceToleranceMultiplier", "type": {"defined": "BorshDecimal"}}, {"name": "consecutiveFeedFailureLimit", "type": "u64"}, {"name": "consecutiveOracleFailureLimit", "type": "u64"}, {"name": "queueSize", "type": "u32"}, {"name": "unpermissionedFeeds", "type": "bool"}]}}, {"name": "OracleQueueSetRewardsParams", "type": {"kind": "struct", "fields": [{"name": "rewards", "type": "u64"}]}}, {"name": "OracleWithdrawParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "permissionBump", "type": "u8"}, {"name": "amount", "type": "u64"}]}}, {"name": "PermissionInitParams", "type": {"kind": "struct", "fields": [{"name": "permissionBump", "type": "u8"}]}}, {"name": "PermissionSetParams", "type": {"kind": "struct", "fields": [{"name": "permission", "type": {"defined": "SwitchboardPermission"}}, {"name": "enable", "type": "bool"}]}}, {"name": "ProgramConfigParams", "type": {"kind": "struct", "fields": [{"name": "token", "type": "publicKey"}, {"name": "bump", "type": "u8"}]}}, {"name": "ProgramInitParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}]}}, {"name": "Hash", "type": {"kind": "struct", "fields": [{"name": "data", "type": {"array": ["u8", 32]}}]}}, {"name": "AggregatorRound", "type": {"kind": "struct", "fields": [{"name": "numSuccess", "type": "u32"}, {"name": "numError", "type": "u32"}, {"name": "isClosed", "type": "bool"}, {"name": "roundOpenSlot", "type": "u64"}, {"name": "roundOpenTimestamp", "type": "i64"}, {"name": "result", "type": {"defined": "SwitchboardDecimal"}}, {"name": "stdDeviation", "type": {"defined": "SwitchboardDecimal"}}, {"name": "minResponse", "type": {"defined": "SwitchboardDecimal"}}, {"name": "maxResponse", "type": {"defined": "SwitchboardDecimal"}}, {"name": "oraclePubkeysData", "type": {"array": ["publicKey", 16]}}, {"name": "mediansData", "type": {"array": [{"defined": "SwitchboardDecimal"}, 16]}}, {"name": "currentPayout", "type": {"array": ["i64", 16]}}, {"name": "mediansFulfilled", "type": {"array": ["bool", 16]}}, {"name": "errorsFulfilled", "type": {"array": ["bool", 16]}}]}}, {"name": "AggregatorHistoryRow", "type": {"kind": "struct", "fields": [{"name": "timestamp", "type": "i64"}, {"name": "value", "type": {"defined": "SwitchboardDecimal"}}]}}, {"name": "SwitchboardDecimal", "type": {"kind": "struct", "fields": [{"name": "mantissa", "type": "i128"}, {"name": "scale", "type": "u32"}]}}, {"name": "VrfRound", "type": {"kind": "struct", "fields": [{"name": "vrfProducer", "type": "publicKey"}, {"name": "reprProof", "type": {"array": ["u8", 224]}}, {"name": "proof", "type": {"array": ["u8", 80]}}, {"name": "alpha", "type": {"array": ["u8", 64]}}, {"name": "alphaLen", "type": "u32"}, {"name": "stage", "type": "u32"}, {"name": "ebuf", "type": {"array": ["u8", 255]}}]}}, {"name": "CrankRow", "type": {"kind": "struct", "fields": [{"name": "pubkey", "type": "publicKey"}, {"name": "nextTimestamp", "type": "i64"}]}}, {"name": "OracleMetrics", "type": {"kind": "struct", "fields": [{"name": "consecutiveSuccess", "type": "u64"}, {"name": "consecutiveError", "type": "u64"}, {"name": "consecutiveDisagreement", "type": "u64"}, {"name": "consecutiveLateResponse", "type": "u64"}, {"name": "consecutiveFailure", "type": "u64"}, {"name": "totalSuccess", "type": "u128"}, {"name": "totalError", "type": "u128"}, {"name": "totalDisagreement", "type": "u128"}, {"name": "totalLateResponse", "type": "u128"}]}}, {"name": "BorshDecimal", "type": {"kind": "struct", "fields": [{"name": "mantissa", "type": "i128"}, {"name": "scale", "type": "u32"}]}}, {"name": "VaultTransferParams", "type": {"kind": "struct", "fields": [{"name": "stateBump", "type": "u8"}, {"name": "amount", "type": "u64"}]}}, {"name": "SwitchboardPermission", "type": {"kind": "enum", "variants": [{"name": "PermitOracleHeartbeat"}, {"name": "PermitOracleQueueUsage"}]}}, {"name": "OracleResponseType", "type": {"kind": "enum", "variants": [{"name": "TypeSuccess"}, {"name": "TypeError"}, {"name": "TypeDisagreement"}, {"name": "TypeNoResponse"}]}}, {"name": "Error", "type": {"kind": "enum", "variants": [{"name": "InvalidPublicKey"}, {"name": "SerializationError", "fields": [{"defined": "bincode::Error"}]}, {"name": "DeserializationError", "fields": [{"defined": "bincode::Error"}]}, {"name": "InvalidDataError"}]}}], "events": [{"name": "AggregatorInitEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}]}, {"name": "AggregatorOpenRoundEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "oraclePubkeys", "type": {"vec": "publicKey"}, "index": false}, {"name": "jobPubkeys", "type": {"vec": "publicKey"}, "index": false}, {"name": "remainingFunds", "type": "u64", "index": false}, {"name": "queueAuthority", "type": "publicKey", "index": false}]}, {"name": "AggregatorValueUpdateEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "value", "type": {"defined": "BorshDecimal"}, "index": false}, {"name": "slot", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}, {"name": "oraclePubkeys", "type": {"vec": "publicKey"}, "index": false}, {"name": "oracleValues", "type": {"vec": {"defined": "BorshDecimal"}}, "index": false}]}, {"name": "OracleRewardEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "leasePubkey", "type": "publicKey", "index": false}, {"name": "oraclePubkey", "type": "publicKey", "index": false}, {"name": "walletPubkey", "type": "publicKey", "index": false}, {"name": "amount", "type": "u64", "index": false}, {"name": "roundSlot", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "OracleWithdrawEvent", "fields": [{"name": "oraclePubkey", "type": "publicKey", "index": false}, {"name": "walletPubkey", "type": "publicKey", "index": false}, {"name": "destinationWallet", "type": "publicKey", "index": false}, {"name": "previousAmount", "type": "u64", "index": false}, {"name": "newAmount", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "LeaseWithdrawEvent", "fields": [{"name": "leasePubkey", "type": "publicKey", "index": false}, {"name": "walletPubkey", "type": "publicKey", "index": false}, {"name": "previousAmount", "type": "u64", "index": false}, {"name": "newAmount", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "OracleSlashEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "leasePubkey", "type": "publicKey", "index": false}, {"name": "oraclePubkey", "type": "publicKey", "index": false}, {"name": "walletPubkey", "type": "publicKey", "index": false}, {"name": "amount", "type": "u64", "index": false}, {"name": "roundSlot", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "LeaseFundEvent", "fields": [{"name": "leasePubkey", "type": "publicKey", "index": false}, {"name": "funder", "type": "publicKey", "index": false}, {"name": "amount", "type": "u64", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "ProbationBrokenEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "queuePubkey", "type": "publicKey", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "FeedPermissionRevokedEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "timestamp", "type": "i64", "index": false}]}, {"name": "GarbageCollectFailureEvent", "fields": [{"name": "queuePubkey", "type": "publicKey", "index": false}]}, {"name": "OracleBootedEvent", "fields": [{"name": "queuePubkey", "type": "publicKey", "index": false}, {"name": "oraclePubkey", "type": "publicKey", "index": false}]}, {"name": "CrankLeaseInsufficientFundsEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "leasePubkey", "type": "publicKey", "index": false}]}, {"name": "CrankPopExpectedFailureEvent", "fields": [{"name": "feedPubkey", "type": "publicKey", "index": false}, {"name": "leasePubkey", "type": "publicKey", "index": false}]}], "errors": [{"code": 6000, "name": "ArrayOperationError", "msg": "Illegal operation on a Switchboard array."}, {"code": 6001, "name": "QueueOperationError", "msg": "Illegal operation on a Switchboard queue."}, {"code": 6002, "name": "IncorrectProgramOwnerError", "msg": "An account required to be owned by the program has a different owner."}, {"code": 6003, "name": "InvalidAggregatorRound", "msg": "Aggregator is not currently populated with a valid round."}, {"code": 6004, "name": "TooManyAggregatorJobs", "msg": "Aggregator cannot fit any more jobs."}, {"code": 6005, "name": "AggregatorCurrentRoundClosed", "msg": "Aggregator's current round is closed. No results are being accepted."}, {"code": 6006, "name": "AggregatorInvalidSaveResult", "msg": "Aggregator received an invalid save result instruction."}, {"code": 6007, "name": "InvalidStrDecimalConversion", "msg": "Failed to convert string to decimal format."}, {"code": 6008, "name": "AccountLoaderMissingSignature", "msg": "AccountLoader account is missing a required signature."}, {"code": 6009, "name": "MissingRequiredSignature", "msg": "Account is missing a required signature."}, {"code": 6010, "name": "ArrayOverflowError", "msg": "The attempted action will overflow a zero-copy account array."}, {"code": 6011, "name": "ArrayUnderflowError", "msg": "The attempted action will underflow a zero-copy account array."}, {"code": 6012, "name": "PubkeyNotFoundError", "msg": "The queried public key was not found."}, {"code": 6013, "name": "AggregatorIllegalRoundOpenCall", "msg": "Aggregator round open called too early."}, {"code": 6014, "name": "AggregatorIllegalRoundCloseCall", "msg": "Aggregator round close called too early."}, {"code": 6015, "name": "AggregatorClosedError", "msg": "Aggregator is closed. Illegal aciton."}, {"code": 6016, "name": "IllegalOracleIdxError", "msg": "Illegal oracle index."}, {"code": 6017, "name": "OracleAlreadyRespondedError", "msg": "The provided oracle has already responded this round."}, {"code": 6018, "name": "ProtoDeserializeError", "msg": "Failed to deserialize protocol buffer."}, {"code": 6019, "name": "UnauthorizedStateUpdateError", "msg": "Unathorized program state modification attempted."}, {"code": 6020, "name": "MissingOracleAccountsError", "msg": "Not enough oracle accounts provided to closeRounds."}, {"code": 6021, "name": "OracleMismatchError", "msg": "An unexpected oracle account was provided for the transaction."}, {"code": 6022, "name": "CrankMaxCapacityError", "msg": "Attempted to push to a Crank that's at capacity"}, {"code": 6023, "name": "AggregatorLeaseInsufficientFunds", "msg": "Aggregator update call attempted but attached lease has insufficient funds."}, {"code": 6024, "name": "IncorrectTokenAccountMint", "msg": "The provided token account does not point to the Switchbaord token mint."}, {"code": 6025, "name": "InvalidEscrowAccount", "msg": "An invalid escrow account was provided."}, {"code": 6026, "name": "CrankEmptyError", "msg": "Crank empty. Pop failed."}, {"code": 6027, "name": "PdaDeriveError", "msg": "Failed to derive a PDA from the provided seed."}, {"code": 6028, "name": "AggregatorAccountNotFound", "msg": "Aggregator account missing from provided account list."}, {"code": 6029, "name": "PermissionAccountNotFound", "msg": "Permission account missing from provided account list."}, {"code": 6030, "name": "LeaseAccountDeriveFailure", "msg": "Failed to derive a lease account."}, {"code": 6031, "name": "PermissionAccountDeriveFailure", "msg": "Failed to derive a permission account."}, {"code": 6032, "name": "EscrowAccountNotFound", "msg": "Escrow account missing from provided account list."}, {"code": 6033, "name": "LeaseAccountNotFound", "msg": "Lease account missing from provided account list."}, {"code": 6034, "name": "DecimalConversionError", "msg": "Decimal conversion method failed."}, {"code": 6035, "name": "PermissionDenied", "msg": "Permission account is missing required flags for the given action."}, {"code": 6036, "name": "QueueAtCapacity", "msg": "Oracle queue is at lease capacity."}, {"code": 6037, "name": "ExcessiveCrankRowsError", "msg": "Data feed is already pushed on a crank."}, {"code": 6038, "name": "AggregatorLockedError", "msg": "Aggregator is locked, no setting modifications or job additions allowed."}, {"code": 6039, "name": "AggregatorInvalidBatchSizeError", "msg": "Aggregator invalid batch size."}, {"code": 6040, "name": "AggregatorJobChecksumMismatch", "msg": "Oracle provided an incorrect aggregator job checksum."}, {"code": 6041, "name": "IntegerOverflowError", "msg": "An integer overflow occurred."}, {"code": 6042, "name": "InvalidUpdatePeriodError", "msg": "Mimimum update period is 5 seconds."}, {"code": 6043, "name": "NoResultsError", "msg": "Aggregator round evaluation attempted with no results."}, {"code": 6044, "name": "InvalidExpirationError", "msg": "An expiration constraint was broken."}, {"code": 6045, "name": "InsufficientStakeError", "msg": "An account provided insufficient stake for aciton."}, {"code": 6046, "name": "LeaseInactiveError", "msg": "The provided lease account is not active."}, {"code": 6047, "name": "NoAggregatorJobsFound", "msg": "No jobs are currently included in the aggregator."}, {"code": 6048, "name": "IntegerUnderflowError", "msg": "An integer underflow occurred."}, {"code": 6049, "name": "OracleQueueMismatch", "msg": "An invalid oracle queue account was provided."}, {"code": 6050, "name": "OracleWalletMismatchError", "msg": "An unexpected oracle wallet account was provided for the transaction."}, {"code": 6051, "name": "InvalidBufferAccountError", "msg": "An invalid buffer account was provided."}, {"code": 6052, "name": "InsufficientOracleQueueError", "msg": "Insufficient oracle queue size."}, {"code": 6053, "name": "InvalidAuthorityError", "msg": "Invalid authority account provided."}, {"code": 6054, "name": "InvalidTokenAccountMintError", "msg": "A provided token wallet is associated with an incorrect mint."}, {"code": 6055, "name": "ExcessiveLeaseWithdrawlError", "msg": "You must leave enough funds to perform at least 1 update in the lease."}, {"code": 6056, "name": "InvalideHistoryAccountError", "msg": "Invalid history account provided."}, {"code": 6057, "name": "InvalidLeaseAccountEscrowError", "msg": "Invalid lease account escrow."}, {"code": 6058, "name": "InvalidCrankAccountError", "msg": "Invalid crank provided."}, {"code": 6059, "name": "CrankNoElementsReadyError", "msg": "No elements ready to be popped."}, {"code": 6060, "name": "VrfVerifyError", "msg": "Error in verifying vrf proof."}]} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/switchboard_v2.mainnet.06022022.json b/basics/anchorpy-main/tests/idls/switchboard_v2.mainnet.06022022.json new file mode 100644 index 0000000..1635cf8 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/switchboard_v2.mainnet.06022022.json @@ -0,0 +1,4601 @@ +{ + "version": "0.1.0", + "name": "switchboard_v2", + "instructions": [ + { + "name": "aggregatorAddJob", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "job", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorAddJobParams" + } + } + ] + }, + { + "name": "aggregatorInit", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + }, + { + "name": "authorWallet", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorInitParams" + } + } + ] + }, + { + "name": "aggregatorLock", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorLockParams" + } + } + ] + }, + { + "name": "aggregatorOpenRound", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "queueAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "permission", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "payoutWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "dataBuffer", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorOpenRoundParams" + } + } + ] + }, + { + "name": "aggregatorRemoveJob", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "job", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorRemoveJobParams" + } + } + ] + }, + { + "name": "aggregatorSaveResult", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "oracleQueue", + "isMut": false, + "isSigner": false + }, + { + "name": "queueAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "feedPermission", + "isMut": true, + "isSigner": false + }, + { + "name": "oraclePermission", + "isMut": false, + "isSigner": false + }, + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "historyBuffer", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSaveResultParams" + } + } + ] + }, + { + "name": "aggregatorSetAuthority", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetAuthorityParams" + } + } + ] + }, + { + "name": "aggregatorSetBatchSize", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetBatchSizeParams" + } + } + ] + }, + { + "name": "aggregatorSetHistoryBuffer", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "buffer", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetHistoryBufferParams" + } + } + ] + }, + { + "name": "aggregatorSetMinJobs", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetMinJobsParams" + } + } + ] + }, + { + "name": "aggregatorSetMinOracles", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetMinOraclesParams" + } + } + ] + }, + { + "name": "aggregatorSetQueue", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetQueueParams" + } + } + ] + }, + { + "name": "aggregatorSetUpdateInterval", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetUpdateIntervalParams" + } + } + ] + }, + { + "name": "aggregatorSetVarianceThreshold", + "accounts": [ + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "AggregatorSetVarianceThresholdParams" + } + } + ] + }, + { + "name": "crankInit", + "accounts": [ + { + "name": "crank", + "isMut": true, + "isSigner": true + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + }, + { + "name": "buffer", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CrankInitParams" + } + } + ] + }, + { + "name": "crankPop", + "accounts": [ + { + "name": "crank", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "queueAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "payoutWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "crankDataBuffer", + "isMut": true, + "isSigner": false + }, + { + "name": "queueDataBuffer", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CrankPopParams" + } + } + ] + }, + { + "name": "crankPush", + "accounts": [ + { + "name": "crank", + "isMut": true, + "isSigner": false + }, + { + "name": "aggregator", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "queueAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "permission", + "isMut": false, + "isSigner": false + }, + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "dataBuffer", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CrankPushParams" + } + } + ] + }, + { + "name": "jobInit", + "accounts": [ + { + "name": "job", + "isMut": true, + "isSigner": false + }, + { + "name": "authorWallet", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "JobInitParams" + } + } + ] + }, + { + "name": "leaseExtend", + "accounts": [ + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + }, + { + "name": "funder", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "LeaseExtendParams" + } + } + ] + }, + { + "name": "leaseInit", + "accounts": [ + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "queue", + "isMut": true, + "isSigner": false + }, + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "funder", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "LeaseInitParams" + } + } + ] + }, + { + "name": "leaseSetAuthority", + "accounts": [ + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "LeaseSetAuthorityParams" + } + } + ] + }, + { + "name": "leaseWithdraw", + "accounts": [ + { + "name": "lease", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + }, + { + "name": "withdrawAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "withdrawAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "LeaseWithdrawParams" + } + } + ] + }, + { + "name": "oracleHeartbeat", + "accounts": [ + { + "name": "oracle", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "gcOracle", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "permission", + "isMut": false, + "isSigner": false + }, + { + "name": "dataBuffer", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleHeartbeatParams" + } + } + ] + }, + { + "name": "oracleInit", + "accounts": [ + { + "name": "oracle", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "wallet", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "queue", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleInitParams" + } + } + ] + }, + { + "name": "oracleQueueInit", + "accounts": [ + { + "name": "oracleQueue", + "isMut": true, + "isSigner": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "buffer", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleQueueInitParams" + } + } + ] + }, + { + "name": "oracleQueueSetRewards", + "accounts": [ + { + "name": "queue", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleQueueSetRewardsParams" + } + } + ] + }, + { + "name": "oracleQueueVrfConfig", + "accounts": [ + { + "name": "queue", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleQueueVrfConfigParams" + } + } + ] + }, + { + "name": "oracleWithdraw", + "accounts": [ + { + "name": "oracle", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "withdrawAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "permission", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OracleWithdrawParams" + } + } + ] + }, + { + "name": "permissionInit", + "accounts": [ + { + "name": "permission", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "granter", + "isMut": false, + "isSigner": false + }, + { + "name": "grantee", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "PermissionInitParams" + } + } + ] + }, + { + "name": "permissionSet", + "accounts": [ + { + "name": "permission", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "PermissionSetParams" + } + } + ] + }, + { + "name": "programConfig", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "ProgramConfigParams" + } + } + ] + }, + { + "name": "programInit", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenMint", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "ProgramInitParams" + } + } + ] + }, + { + "name": "vaultTransfer", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "to", + "isMut": true, + "isSigner": false + }, + { + "name": "vault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VaultTransferParams" + } + } + ] + }, + { + "name": "vrfInit", + "accounts": [ + { + "name": "vrf", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": false, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VrfInitParams" + } + } + ] + }, + { + "name": "vrfProve", + "accounts": [ + { + "name": "vrf", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "randomnessProducer", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VrfProveParams" + } + } + ] + }, + { + "name": "vrfProveAndVerify", + "accounts": [ + { + "name": "vrf", + "isMut": true, + "isSigner": false + }, + { + "name": "callbackPid", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "oracleWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "instructionsSysvar", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VrfProveAndVerifyParams" + } + } + ] + }, + { + "name": "vrfRequestRandomness", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "vrf", + "isMut": true, + "isSigner": false + }, + { + "name": "oracleQueue", + "isMut": true, + "isSigner": false + }, + { + "name": "queueAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "dataBuffer", + "isMut": false, + "isSigner": false + }, + { + "name": "permission", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "payerWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "payerAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "recentBlockhashes", + "isMut": false, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VrfRequestRandomnessParams" + } + } + ] + }, + { + "name": "vrfVerify", + "accounts": [ + { + "name": "vrf", + "isMut": true, + "isSigner": false + }, + { + "name": "callbackPid", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "programState", + "isMut": false, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "oracleAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "oracleWallet", + "isMut": true, + "isSigner": false + }, + { + "name": "instructionsSysvar", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "VrfVerifyParams" + } + } + ] + } + ], + "accounts": [ + { + "name": "SbState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "tokenMint", + "type": "publicKey" + }, + { + "name": "tokenVault", + "type": "publicKey" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 1024] + } + } + ] + } + }, + { + "name": "AggregatorAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 128] + } + }, + { + "name": "authorWallet", + "type": "publicKey" + }, + { + "name": "queuePubkey", + "type": "publicKey" + }, + { + "name": "oracleRequestBatchSize", + "type": "u32" + }, + { + "name": "minOracleResults", + "type": "u32" + }, + { + "name": "minJobResults", + "type": "u32" + }, + { + "name": "minUpdateDelaySeconds", + "type": "u32" + }, + { + "name": "startAfter", + "type": "i64" + }, + { + "name": "varianceThreshold", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "forceReportPeriod", + "type": "i64" + }, + { + "name": "expiration", + "type": "i64" + }, + { + "name": "consecutiveFailureCount", + "type": "u64" + }, + { + "name": "nextAllowedUpdateTime", + "type": "i64" + }, + { + "name": "isLocked", + "type": "bool" + }, + { + "name": "crankPubkey", + "type": "publicKey" + }, + { + "name": "latestConfirmedRound", + "type": { + "defined": "AggregatorRound" + } + }, + { + "name": "currentRound", + "type": { + "defined": "AggregatorRound" + } + }, + { + "name": "jobPubkeysData", + "type": { + "array": ["publicKey", 16] + } + }, + { + "name": "jobHashes", + "type": { + "array": [ + { + "defined": "Hash" + }, + 16 + ] + } + }, + { + "name": "jobPubkeysSize", + "type": "u32" + }, + { + "name": "jobsChecksum", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "historyBuffer", + "type": "publicKey" + }, + { + "name": "previousConfirmedRoundResult", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "previousConfirmedRoundSlot", + "type": "u64" + }, + { + "name": "disableCrank", + "type": "bool" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 163] + } + } + ] + } + }, + { + "name": "PermissionAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "permissions", + "type": "u32" + }, + { + "name": "granter", + "type": "publicKey" + }, + { + "name": "grantee", + "type": "publicKey" + }, + { + "name": "expiration", + "type": "i64" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 256] + } + } + ] + } + }, + { + "name": "LeaseAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "publicKey" + }, + { + "name": "queue", + "type": "publicKey" + }, + { + "name": "aggregator", + "type": "publicKey" + }, + { + "name": "tokenProgram", + "type": "publicKey" + }, + { + "name": "isActive", + "type": "bool" + }, + { + "name": "crankRowCount", + "type": "u32" + }, + { + "name": "createdAt", + "type": "i64" + }, + { + "name": "updateCount", + "type": "u128" + }, + { + "name": "withdrawAuthority", + "type": "publicKey" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 256] + } + } + ] + } + }, + { + "name": "OracleQueueAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 64] + } + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "oracleTimeout", + "type": "u32" + }, + { + "name": "reward", + "type": "u64" + }, + { + "name": "minStake", + "type": "u64" + }, + { + "name": "slashingEnabled", + "type": "bool" + }, + { + "name": "varianceToleranceMultiplier", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "feedProbationPeriod", + "type": "u32" + }, + { + "name": "currIdx", + "type": "u32" + }, + { + "name": "size", + "type": "u32" + }, + { + "name": "gcIdx", + "type": "u32" + }, + { + "name": "consecutiveFeedFailureLimit", + "type": "u64" + }, + { + "name": "consecutiveOracleFailureLimit", + "type": "u64" + }, + { + "name": "unpermissionedFeedsEnabled", + "type": "bool" + }, + { + "name": "unpermissionedVrfEnabled", + "type": "bool" + }, + { + "name": "curatorRewardCut", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "lockLeaseFunding", + "type": "bool" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 1001] + } + }, + { + "name": "maxSize", + "type": "u32" + }, + { + "name": "dataBuffer", + "type": "publicKey" + } + ] + } + }, + { + "name": "CrankAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 64] + } + }, + { + "name": "queuePubkey", + "type": "publicKey" + }, + { + "name": "pqSize", + "type": "u32" + }, + { + "name": "maxRows", + "type": "u32" + }, + { + "name": "jitterModifier", + "type": "u8" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 255] + } + }, + { + "name": "dataBuffer", + "type": "publicKey" + } + ] + } + }, + { + "name": "OracleAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 128] + } + }, + { + "name": "oracleAuthority", + "type": "publicKey" + }, + { + "name": "lastHeartbeat", + "type": "i64" + }, + { + "name": "numInUse", + "type": "u32" + }, + { + "name": "tokenAccount", + "type": "publicKey" + }, + { + "name": "queuePubkey", + "type": "publicKey" + }, + { + "name": "metrics", + "type": { + "defined": "OracleMetrics" + } + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 256] + } + } + ] + } + }, + { + "name": "JobAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 64] + } + }, + { + "name": "authorWallet", + "type": "publicKey" + }, + { + "name": "expiration", + "type": "i64" + }, + { + "name": "hash", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "referenceCount", + "type": "u32" + }, + { + "name": "totalSpent", + "type": "u128" + } + ] + } + }, + { + "name": "VrfAccountData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "status", + "type": { + "defined": "VrfStatus" + } + }, + { + "name": "counter", + "type": "u128" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "oracleQueue", + "type": "publicKey" + }, + { + "name": "escrow", + "type": "publicKey" + }, + { + "name": "callback", + "type": { + "defined": "CallbackZC" + } + }, + { + "name": "batchSize", + "type": "u32" + }, + { + "name": "builders", + "type": { + "array": [ + { + "defined": "VrfBuilder" + }, + 8 + ] + } + }, + { + "name": "buildersLen", + "type": "u32" + }, + { + "name": "testMode", + "type": "bool" + }, + { + "name": "currentRound", + "type": { + "defined": "VrfRound" + } + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 1024] + } + } + ] + } + } + ], + "types": [ + { + "name": "AggregatorAddJobParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "AggregatorInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 128] + } + }, + { + "name": "batchSize", + "type": "u32" + }, + { + "name": "minOracleResults", + "type": "u32" + }, + { + "name": "minJobResults", + "type": "u32" + }, + { + "name": "minUpdateDelaySeconds", + "type": "u32" + }, + { + "name": "startAfter", + "type": "i64" + }, + { + "name": "varianceThreshold", + "type": { + "defined": "BorshDecimal" + } + }, + { + "name": "forceReportPeriod", + "type": "i64" + }, + { + "name": "expiration", + "type": "i64" + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "AggregatorLockParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "AggregatorOpenRoundParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "leaseBump", + "type": "u8" + }, + { + "name": "permissionBump", + "type": "u8" + }, + { + "name": "jitter", + "type": "u8" + } + ] + } + }, + { + "name": "AggregatorRemoveJobParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "jobIdx", + "type": "u32" + } + ] + } + }, + { + "name": "AggregatorSaveResultParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "oracleIdx", + "type": "u32" + }, + { + "name": "error", + "type": "bool" + }, + { + "name": "value", + "type": { + "defined": "BorshDecimal" + } + }, + { + "name": "jobsChecksum", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "minResponse", + "type": { + "defined": "BorshDecimal" + } + }, + { + "name": "maxResponse", + "type": { + "defined": "BorshDecimal" + } + }, + { + "name": "feedPermissionBump", + "type": "u8" + }, + { + "name": "oraclePermissionBump", + "type": "u8" + }, + { + "name": "leaseBump", + "type": "u8" + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "AggregatorSetAuthorityParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "AggregatorSetBatchSizeParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "batchSize", + "type": "u32" + } + ] + } + }, + { + "name": "AggregatorSetHistoryBufferParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "AggregatorSetMinJobsParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "minJobResults", + "type": "u32" + } + ] + } + }, + { + "name": "AggregatorSetMinOraclesParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "minOracleResults", + "type": "u32" + } + ] + } + }, + { + "name": "AggregatorSetQueueParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "AggregatorSetUpdateIntervalParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "newInterval", + "type": "u32" + } + ] + } + }, + { + "name": "AggregatorSetVarianceThresholdParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "varianceThreshold", + "type": { + "defined": "BorshDecimal" + } + } + ] + } + }, + { + "name": "CrankInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "bytes" + }, + { + "name": "metadata", + "type": "bytes" + }, + { + "name": "crankSize", + "type": "u32" + } + ] + } + }, + { + "name": "CrankPopParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "leaseBumps", + "type": "bytes" + }, + { + "name": "permissionBumps", + "type": "bytes" + }, + { + "name": "nonce", + "type": { + "option": "u32" + } + }, + { + "name": "failOpenOnAccountMismatch", + "type": { + "option": "bool" + } + } + ] + } + }, + { + "name": "CrankPushParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "permissionBump", + "type": "u8" + } + ] + } + }, + { + "name": "JobInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "expiration", + "type": "i64" + }, + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "data", + "type": "bytes" + } + ] + } + }, + { + "name": "LeaseExtendParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "loadAmount", + "type": "u64" + }, + { + "name": "leaseBump", + "type": "u8" + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "LeaseInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "loadAmount", + "type": "u64" + }, + { + "name": "withdrawAuthority", + "type": "publicKey" + }, + { + "name": "leaseBump", + "type": "u8" + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "LeaseSetAuthorityParams", + "type": { + "kind": "struct", + "fields": [] + } + }, + { + "name": "LeaseWithdrawParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "leaseBump", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "OracleHeartbeatParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "permissionBump", + "type": "u8" + } + ] + } + }, + { + "name": "OracleInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "bytes" + }, + { + "name": "metadata", + "type": "bytes" + }, + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "oracleBump", + "type": "u8" + } + ] + } + }, + { + "name": "OracleQueueInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "metadata", + "type": { + "array": ["u8", 64] + } + }, + { + "name": "reward", + "type": "u64" + }, + { + "name": "minStake", + "type": "u64" + }, + { + "name": "feedProbationPeriod", + "type": "u32" + }, + { + "name": "oracleTimeout", + "type": "u32" + }, + { + "name": "slashingEnabled", + "type": "bool" + }, + { + "name": "varianceToleranceMultiplier", + "type": { + "defined": "BorshDecimal" + } + }, + { + "name": "consecutiveFeedFailureLimit", + "type": "u64" + }, + { + "name": "consecutiveOracleFailureLimit", + "type": "u64" + }, + { + "name": "queueSize", + "type": "u32" + }, + { + "name": "unpermissionedFeeds", + "type": "bool" + }, + { + "name": "unpermissionedVrf", + "type": "bool" + } + ] + } + }, + { + "name": "OracleQueueSetRewardsParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rewards", + "type": "u64" + } + ] + } + }, + { + "name": "OracleQueueVrfConfigParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "unpermissionedVrfEnabled", + "type": "bool" + } + ] + } + }, + { + "name": "OracleWithdrawParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "permissionBump", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "PermissionInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "permissionBump", + "type": "u8" + } + ] + } + }, + { + "name": "PermissionSetParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "permission", + "type": { + "defined": "SwitchboardPermission" + } + }, + { + "name": "enable", + "type": "bool" + } + ] + } + }, + { + "name": "ProgramConfigParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "token", + "type": "publicKey" + }, + { + "name": "bump", + "type": "u8" + } + ] + } + }, + { + "name": "ProgramInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "VaultTransferParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "VrfInitParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "callback", + "type": { + "defined": "Callback" + } + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "VrfProveParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "proof", + "type": "bytes" + }, + { + "name": "idx", + "type": "u32" + } + ] + } + }, + { + "name": "VrfProveAndVerifyParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "nonce", + "type": { + "option": "u32" + } + }, + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "idx", + "type": "u32" + }, + { + "name": "proof", + "type": "bytes" + } + ] + } + }, + { + "name": "VrfRequestRandomnessParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "permissionBump", + "type": "u8" + }, + { + "name": "stateBump", + "type": "u8" + } + ] + } + }, + { + "name": "VrfVerifyParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "nonce", + "type": { + "option": "u32" + } + }, + { + "name": "stateBump", + "type": "u8" + }, + { + "name": "idx", + "type": "u32" + } + ] + } + }, + { + "name": "Hash", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": { + "array": ["u8", 32] + } + } + ] + } + }, + { + "name": "AggregatorRound", + "type": { + "kind": "struct", + "fields": [ + { + "name": "numSuccess", + "type": "u32" + }, + { + "name": "numError", + "type": "u32" + }, + { + "name": "isClosed", + "type": "bool" + }, + { + "name": "roundOpenSlot", + "type": "u64" + }, + { + "name": "roundOpenTimestamp", + "type": "i64" + }, + { + "name": "result", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "stdDeviation", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "minResponse", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "maxResponse", + "type": { + "defined": "SwitchboardDecimal" + } + }, + { + "name": "oraclePubkeysData", + "type": { + "array": ["publicKey", 16] + } + }, + { + "name": "mediansData", + "type": { + "array": [ + { + "defined": "SwitchboardDecimal" + }, + 16 + ] + } + }, + { + "name": "currentPayout", + "type": { + "array": ["i64", 16] + } + }, + { + "name": "mediansFulfilled", + "type": { + "array": ["bool", 16] + } + }, + { + "name": "errorsFulfilled", + "type": { + "array": ["bool", 16] + } + } + ] + } + }, + { + "name": "AggregatorHistoryRow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "value", + "type": { + "defined": "SwitchboardDecimal" + } + } + ] + } + }, + { + "name": "SwitchboardDecimal", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mantissa", + "type": "i128" + }, + { + "name": "scale", + "type": "u32" + } + ] + } + }, + { + "name": "CrankRow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "nextTimestamp", + "type": "i64" + } + ] + } + }, + { + "name": "OracleMetrics", + "type": { + "kind": "struct", + "fields": [ + { + "name": "consecutiveSuccess", + "type": "u64" + }, + { + "name": "consecutiveError", + "type": "u64" + }, + { + "name": "consecutiveDisagreement", + "type": "u64" + }, + { + "name": "consecutiveLateResponse", + "type": "u64" + }, + { + "name": "consecutiveFailure", + "type": "u64" + }, + { + "name": "totalSuccess", + "type": "u128" + }, + { + "name": "totalError", + "type": "u128" + }, + { + "name": "totalDisagreement", + "type": "u128" + }, + { + "name": "totalLateResponse", + "type": "u128" + } + ] + } + }, + { + "name": "BorshDecimal", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mantissa", + "type": "i128" + }, + { + "name": "scale", + "type": "u32" + } + ] + } + }, + { + "name": "EcvrfProofZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "gamma", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "c", + "type": { + "defined": "Scalar" + } + }, + { + "name": "s", + "type": { + "defined": "Scalar" + } + } + ] + } + }, + { + "name": "Scalar", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bytes", + "type": { + "array": ["u8", 32] + } + } + ] + } + }, + { + "name": "FieldElementZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bytes", + "type": { + "array": ["u64", 5] + } + } + ] + } + }, + { + "name": "CompletedPointZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "x", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "y", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "z", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "t", + "type": { + "defined": "FieldElementZC" + } + } + ] + } + }, + { + "name": "EdwardsPointZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "x", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "y", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "z", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "t", + "type": { + "defined": "FieldElementZC" + } + } + ] + } + }, + { + "name": "ProjectivePointZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "x", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "y", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "z", + "type": { + "defined": "FieldElementZC" + } + } + ] + } + }, + { + "name": "EcvrfIntermediate", + "type": { + "kind": "struct", + "fields": [ + { + "name": "r", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "nS", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "d", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "t13", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "t15", + "type": { + "defined": "FieldElementZC" + } + } + ] + } + }, + { + "name": "VrfBuilder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "producer", + "type": "publicKey" + }, + { + "name": "status", + "type": { + "defined": "VrfStatus" + } + }, + { + "name": "reprProof", + "type": { + "array": ["u8", 80] + } + }, + { + "name": "proof", + "type": { + "defined": "EcvrfProofZC" + } + }, + { + "name": "yPoint", + "type": "publicKey" + }, + { + "name": "stage", + "type": "u32" + }, + { + "name": "stage1Out", + "type": { + "defined": "EcvrfIntermediate" + } + }, + { + "name": "r1", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "r2", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "stage3Out", + "type": { + "defined": "EcvrfIntermediate" + } + }, + { + "name": "hPoint", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "sReduced", + "type": { + "defined": "Scalar" + } + }, + { + "name": "yPointBuilder", + "type": { + "array": [ + { + "defined": "FieldElementZC" + }, + 3 + ] + } + }, + { + "name": "yRistrettoPoint", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "mulRound", + "type": "u8" + }, + { + "name": "hashPointsRound", + "type": "u8" + }, + { + "name": "mulTmp1", + "type": { + "defined": "CompletedPointZC" + } + }, + { + "name": "uPoint1", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "uPoint2", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "vPoint1", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "vPoint2", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "uPoint", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "vPoint", + "type": { + "defined": "EdwardsPointZC" + } + }, + { + "name": "u1", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "u2", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "invertee", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "y", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "z", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "p1Bytes", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "p2Bytes", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "p3Bytes", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "p4Bytes", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "cPrimeHashbuf", + "type": { + "array": ["u8", 16] + } + }, + { + "name": "m1", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "m2", + "type": { + "defined": "FieldElementZC" + } + }, + { + "name": "txRemaining", + "type": "u32" + }, + { + "name": "verified", + "type": "bool" + }, + { + "name": "result", + "type": { + "array": ["u8", 32] + } + } + ] + } + }, + { + "name": "AccountMetaZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "isSigner", + "type": "bool" + }, + { + "name": "isWritable", + "type": "bool" + } + ] + } + }, + { + "name": "AccountMetaBorsh", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "isSigner", + "type": "bool" + }, + { + "name": "isWritable", + "type": "bool" + } + ] + } + }, + { + "name": "CallbackZC", + "type": { + "kind": "struct", + "fields": [ + { + "name": "programId", + "type": "publicKey" + }, + { + "name": "accounts", + "type": { + "array": [ + { + "defined": "AccountMetaZC" + }, + 32 + ] + } + }, + { + "name": "accountsLen", + "type": "u32" + }, + { + "name": "ixData", + "type": { + "array": ["u8", 1024] + } + }, + { + "name": "ixDataLen", + "type": "u32" + } + ] + } + }, + { + "name": "Callback", + "type": { + "kind": "struct", + "fields": [ + { + "name": "programId", + "type": "publicKey" + }, + { + "name": "accounts", + "type": { + "vec": { + "defined": "AccountMetaBorsh" + } + } + }, + { + "name": "ixData", + "type": "bytes" + } + ] + } + }, + { + "name": "VrfRound", + "type": { + "kind": "struct", + "fields": [ + { + "name": "alpha", + "type": { + "array": ["u8", 256] + } + }, + { + "name": "alphaLen", + "type": "u32" + }, + { + "name": "requestSlot", + "type": "u64" + }, + { + "name": "requestTimestamp", + "type": "i64" + }, + { + "name": "result", + "type": { + "array": ["u8", 32] + } + }, + { + "name": "numVerified", + "type": "u32" + }, + { + "name": "ebuf", + "type": { + "array": ["u8", 256] + } + } + ] + } + }, + { + "name": "Lanes", + "type": { + "kind": "enum", + "variants": [ + { + "name": "C" + }, + { + "name": "D" + }, + { + "name": "AB" + }, + { + "name": "AC" + }, + { + "name": "CD" + }, + { + "name": "AD" + }, + { + "name": "BC" + }, + { + "name": "ABCD" + } + ] + } + }, + { + "name": "Shuffle", + "type": { + "kind": "enum", + "variants": [ + { + "name": "AAAA" + }, + { + "name": "BBBB" + }, + { + "name": "CACA" + }, + { + "name": "DBBD" + }, + { + "name": "ADDA" + }, + { + "name": "CBCB" + }, + { + "name": "ABAB" + }, + { + "name": "BADC" + }, + { + "name": "BACD" + }, + { + "name": "ABDC" + } + ] + } + }, + { + "name": "Shuffle", + "type": { + "kind": "enum", + "variants": [ + { + "name": "AAAA" + }, + { + "name": "BBBB" + }, + { + "name": "BADC" + }, + { + "name": "BACD" + }, + { + "name": "ADDA" + }, + { + "name": "CBCB" + }, + { + "name": "ABDC" + }, + { + "name": "ABAB" + }, + { + "name": "DBBD" + }, + { + "name": "CACA" + } + ] + } + }, + { + "name": "Lanes", + "type": { + "kind": "enum", + "variants": [ + { + "name": "D" + }, + { + "name": "C" + }, + { + "name": "AB" + }, + { + "name": "AC" + }, + { + "name": "AD" + }, + { + "name": "BCD" + } + ] + } + }, + { + "name": "SwitchboardPermission", + "type": { + "kind": "enum", + "variants": [ + { + "name": "PermitOracleHeartbeat" + }, + { + "name": "PermitOracleQueueUsage" + }, + { + "name": "PermitVrfRequests" + } + ] + } + }, + { + "name": "OracleResponseType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "TypeSuccess" + }, + { + "name": "TypeError" + }, + { + "name": "TypeDisagreement" + }, + { + "name": "TypeNoResponse" + } + ] + } + }, + { + "name": "VrfStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "StatusNone" + }, + { + "name": "StatusRequesting" + }, + { + "name": "StatusVerifying" + }, + { + "name": "StatusVerified" + }, + { + "name": "StatusCallbackSuccess" + }, + { + "name": "StatusVerifyFailure" + } + ] + } + } + ], + "events": [ + { + "name": "AggregatorInitEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "VrfRequestRandomnessEvent", + "fields": [ + { + "name": "vrfPubkey", + "type": "publicKey", + "index": true + }, + { + "name": "oraclePubkeys", + "type": { + "vec": "publicKey" + }, + "index": false + }, + { + "name": "loadAmount", + "type": "u64", + "index": false + }, + { + "name": "existingAmount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "VrfRequestEvent", + "fields": [ + { + "name": "vrfPubkey", + "type": "publicKey", + "index": true + }, + { + "name": "oraclePubkeys", + "type": { + "vec": "publicKey" + }, + "index": false + } + ] + }, + { + "name": "VrfProveEvent", + "fields": [ + { + "name": "vrfPubkey", + "type": "publicKey", + "index": true + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": true + }, + { + "name": "authorityPubkey", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "VrfVerifyEvent", + "fields": [ + { + "name": "vrfPubkey", + "type": "publicKey", + "index": true + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": true + }, + { + "name": "authorityPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "VrfCallbackPerformedEvent", + "fields": [ + { + "name": "vrfPubkey", + "type": "publicKey", + "index": true + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": true + }, + { + "name": "amount", + "type": "u64", + "index": false + } + ] + }, + { + "name": "AggregatorOpenRoundEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "oraclePubkeys", + "type": { + "vec": "publicKey" + }, + "index": false + }, + { + "name": "jobPubkeys", + "type": { + "vec": "publicKey" + }, + "index": false + }, + { + "name": "remainingFunds", + "type": "u64", + "index": false + }, + { + "name": "queueAuthority", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "AggregatorValueUpdateEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "value", + "type": { + "defined": "BorshDecimal" + }, + "index": false + }, + { + "name": "slot", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + }, + { + "name": "oraclePubkeys", + "type": { + "vec": "publicKey" + }, + "index": false + }, + { + "name": "oracleValues", + "type": { + "vec": { + "defined": "BorshDecimal" + } + }, + "index": false + } + ] + }, + { + "name": "OracleRewardEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "walletPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "roundSlot", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "OracleWithdrawEvent", + "fields": [ + { + "name": "oraclePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "walletPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "destinationWallet", + "type": "publicKey", + "index": false + }, + { + "name": "previousAmount", + "type": "u64", + "index": false + }, + { + "name": "newAmount", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "LeaseWithdrawEvent", + "fields": [ + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "walletPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "previousAmount", + "type": "u64", + "index": false + }, + { + "name": "newAmount", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "OracleSlashEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "walletPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "roundSlot", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "LeaseFundEvent", + "fields": [ + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "funder", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "ProbationBrokenEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "queuePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "FeedPermissionRevokedEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "timestamp", + "type": "i64", + "index": false + } + ] + }, + { + "name": "GarbageCollectFailureEvent", + "fields": [ + { + "name": "queuePubkey", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "OracleBootedEvent", + "fields": [ + { + "name": "queuePubkey", + "type": "publicKey", + "index": false + }, + { + "name": "oraclePubkey", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "CrankLeaseInsufficientFundsEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "CrankPopExpectedFailureEvent", + "fields": [ + { + "name": "feedPubkey", + "type": "publicKey", + "index": false + }, + { + "name": "leasePubkey", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "ArrayOperationError", + "msg": "Illegal operation on a Switchboard array." + }, + { + "code": 6001, + "name": "QueueOperationError", + "msg": "Illegal operation on a Switchboard queue." + }, + { + "code": 6002, + "name": "IncorrectProgramOwnerError", + "msg": "An account required to be owned by the program has a different owner." + }, + { + "code": 6003, + "name": "InvalidAggregatorRound", + "msg": "Aggregator is not currently populated with a valid round." + }, + { + "code": 6004, + "name": "TooManyAggregatorJobs", + "msg": "Aggregator cannot fit any more jobs." + }, + { + "code": 6005, + "name": "AggregatorCurrentRoundClosed", + "msg": "Aggregator's current round is closed. No results are being accepted." + }, + { + "code": 6006, + "name": "AggregatorInvalidSaveResult", + "msg": "Aggregator received an invalid save result instruction." + }, + { + "code": 6007, + "name": "InvalidStrDecimalConversion", + "msg": "Failed to convert string to decimal format." + }, + { + "code": 6008, + "name": "AccountLoaderMissingSignature", + "msg": "AccountLoader account is missing a required signature." + }, + { + "code": 6009, + "name": "MissingRequiredSignature", + "msg": "Account is missing a required signature." + }, + { + "code": 6010, + "name": "ArrayOverflowError", + "msg": "The attempted action will overflow a zero-copy account array." + }, + { + "code": 6011, + "name": "ArrayUnderflowError", + "msg": "The attempted action will underflow a zero-copy account array." + }, + { + "code": 6012, + "name": "PubkeyNotFoundError", + "msg": "The queried public key was not found." + }, + { + "code": 6013, + "name": "AggregatorIllegalRoundOpenCall", + "msg": "Aggregator round open called too early." + }, + { + "code": 6014, + "name": "AggregatorIllegalRoundCloseCall", + "msg": "Aggregator round close called too early." + }, + { + "code": 6015, + "name": "AggregatorClosedError", + "msg": "Aggregator is closed. Illegal action." + }, + { + "code": 6016, + "name": "IllegalOracleIdxError", + "msg": "Illegal oracle index." + }, + { + "code": 6017, + "name": "OracleAlreadyRespondedError", + "msg": "The provided oracle has already responded this round." + }, + { + "code": 6018, + "name": "ProtoDeserializeError", + "msg": "Failed to deserialize protocol buffer." + }, + { + "code": 6019, + "name": "UnauthorizedStateUpdateError", + "msg": "Unauthorized program state modification attempted." + }, + { + "code": 6020, + "name": "MissingOracleAccountsError", + "msg": "Not enough oracle accounts provided to closeRounds." + }, + { + "code": 6021, + "name": "OracleMismatchError", + "msg": "An unexpected oracle account was provided for the transaction." + }, + { + "code": 6022, + "name": "CrankMaxCapacityError", + "msg": "Attempted to push to a Crank that's at capacity" + }, + { + "code": 6023, + "name": "AggregatorLeaseInsufficientFunds", + "msg": "Aggregator update call attempted but attached lease has insufficient funds." + }, + { + "code": 6024, + "name": "IncorrectTokenAccountMint", + "msg": "The provided token account does not point to the Switchboard token mint." + }, + { + "code": 6025, + "name": "InvalidEscrowAccount", + "msg": "An invalid escrow account was provided." + }, + { + "code": 6026, + "name": "CrankEmptyError", + "msg": "Crank empty. Pop failed." + }, + { + "code": 6027, + "name": "PdaDeriveError", + "msg": "Failed to derive a PDA from the provided seed." + }, + { + "code": 6028, + "name": "AggregatorAccountNotFound", + "msg": "Aggregator account missing from provided account list." + }, + { + "code": 6029, + "name": "PermissionAccountNotFound", + "msg": "Permission account missing from provided account list." + }, + { + "code": 6030, + "name": "LeaseAccountDeriveFailure", + "msg": "Failed to derive a lease account." + }, + { + "code": 6031, + "name": "PermissionAccountDeriveFailure", + "msg": "Failed to derive a permission account." + }, + { + "code": 6032, + "name": "EscrowAccountNotFound", + "msg": "Escrow account missing from provided account list." + }, + { + "code": 6033, + "name": "LeaseAccountNotFound", + "msg": "Lease account missing from provided account list." + }, + { + "code": 6034, + "name": "DecimalConversionError", + "msg": "Decimal conversion method failed." + }, + { + "code": 6035, + "name": "PermissionDenied", + "msg": "Permission account is missing required flags for the given action." + }, + { + "code": 6036, + "name": "QueueAtCapacity", + "msg": "Oracle queue is at lease capacity." + }, + { + "code": 6037, + "name": "ExcessiveCrankRowsError", + "msg": "Data feed is already pushed on a crank." + }, + { + "code": 6038, + "name": "AggregatorLockedError", + "msg": "Aggregator is locked, no setting modifications or job additions allowed." + }, + { + "code": 6039, + "name": "AggregatorInvalidBatchSizeError", + "msg": "Aggregator invalid batch size." + }, + { + "code": 6040, + "name": "AggregatorJobChecksumMismatch", + "msg": "Oracle provided an incorrect aggregator job checksum." + }, + { + "code": 6041, + "name": "IntegerOverflowError", + "msg": "An integer overflow occurred." + }, + { + "code": 6042, + "name": "InvalidUpdatePeriodError", + "msg": "Minimum update period is 5 seconds." + }, + { + "code": 6043, + "name": "NoResultsError", + "msg": "Aggregator round evaluation attempted with no results." + }, + { + "code": 6044, + "name": "InvalidExpirationError", + "msg": "An expiration constraint was broken." + }, + { + "code": 6045, + "name": "InsufficientStakeError", + "msg": "An account provided insufficient stake for action." + }, + { + "code": 6046, + "name": "LeaseInactiveError", + "msg": "The provided lease account is not active." + }, + { + "code": 6047, + "name": "NoAggregatorJobsFound", + "msg": "No jobs are currently included in the aggregator." + }, + { + "code": 6048, + "name": "IntegerUnderflowError", + "msg": "An integer underflow occurred." + }, + { + "code": 6049, + "name": "OracleQueueMismatch", + "msg": "An invalid oracle queue account was provided." + }, + { + "code": 6050, + "name": "OracleWalletMismatchError", + "msg": "An unexpected oracle wallet account was provided for the transaction." + }, + { + "code": 6051, + "name": "InvalidBufferAccountError", + "msg": "An invalid buffer account was provided." + }, + { + "code": 6052, + "name": "InsufficientOracleQueueError", + "msg": "Insufficient oracle queue size." + }, + { + "code": 6053, + "name": "InvalidAuthorityError", + "msg": "Invalid authority account provided." + }, + { + "code": 6054, + "name": "InvalidTokenAccountMintError", + "msg": "A provided token wallet is associated with an incorrect mint." + }, + { + "code": 6055, + "name": "ExcessiveLeaseWithdrawlError", + "msg": "You must leave enough funds to perform at least 1 update in the lease." + }, + { + "code": 6056, + "name": "InvalideHistoryAccountError", + "msg": "Invalid history account provided." + }, + { + "code": 6057, + "name": "InvalidLeaseAccountEscrowError", + "msg": "Invalid lease account escrow." + }, + { + "code": 6058, + "name": "InvalidCrankAccountError", + "msg": "Invalid crank provided." + }, + { + "code": 6059, + "name": "CrankNoElementsReadyError", + "msg": "No elements ready to be popped." + }, + { + "code": 6060, + "name": "IndexOutOfBoundsError", + "msg": "Index out of bounds" + }, + { + "code": 6061, + "name": "VrfInvalidRequestError", + "msg": "Invalid vrf request params" + }, + { + "code": 6062, + "name": "VrfInvalidProofSubmissionError", + "msg": "Vrf proof failed to verify" + }, + { + "code": 6063, + "name": "VrfVerifyError", + "msg": "Error in verifying vrf proof." + }, + { + "code": 6064, + "name": "VrfCallbackError", + "msg": "Vrf callback function failed." + }, + { + "code": 6065, + "name": "VrfCallbackParamsError", + "msg": "Invalid vrf callback params provided." + }, + { + "code": 6066, + "name": "VrfCallbackAlreadyCalledError", + "msg": "Vrf callback has already been triggered." + }, + { + "code": 6067, + "name": "VrfInvalidPubkeyError", + "msg": "The provided pubkey is invalid to use in ecvrf proofs" + }, + { + "code": 6068, + "name": "VrfTooManyVerifyCallsError", + "msg": "Number of required verify calls exceeded" + }, + { + "code": 6069, + "name": "VrfRequestAlreadyLaunchedError", + "msg": "Vrf request is already pending" + }, + { + "code": 6070, + "name": "VrfInsufficientVerificationError", + "msg": "Insufficient amount of proofs collected for VRF callback" + }, + { + "code": 6071, + "name": "InvalidVrfProducerError", + "msg": "An incorrect oracle attempted to submit a proof" + }, + { + "code": 6072, + "name": "NoopError", + "msg": "Noop error" + } + ] +} diff --git a/basics/anchorpy-main/tests/idls/sysvars.json b/basics/anchorpy-main/tests/idls/sysvars.json new file mode 100644 index 0000000..11d7c4b --- /dev/null +++ b/basics/anchorpy-main/tests/idls/sysvars.json @@ -0,0 +1,27 @@ +{ + "version": "0.0.0", + "name": "sysvars", + "instructions": [ + { + "name": "sysvars", + "accounts": [ + { + "name": "clock", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "stakeHistory", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ] +} \ No newline at end of file diff --git a/basics/anchorpy-main/tests/idls/tictactoe.json b/basics/anchorpy-main/tests/idls/tictactoe.json new file mode 100644 index 0000000..507f863 --- /dev/null +++ b/basics/anchorpy-main/tests/idls/tictactoe.json @@ -0,0 +1,179 @@ +{ + "version": "0.1.0", + "name": "tic_tac_toe", + "instructions": [ + { + "name": "setupGame", + "accounts": [ + { + "name": "game", + "isMut": true, + "isSigner": true + }, + { + "name": "playerOne", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "playerTwo", + "type": "publicKey" + } + ] + }, + { + "name": "play", + "accounts": [ + { + "name": "game", + "isMut": true, + "isSigner": false + }, + { + "name": "player", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "tile", + "type": { + "defined": "Tile" + } + } + ] + } + ], + "accounts": [ + { + "name": "Game", + "type": { + "kind": "struct", + "fields": [ + { + "name": "players", + "type": { + "array": ["publicKey", 2] + } + }, + { + "name": "turn", + "type": "u8" + }, + { + "name": "board", + "type": { + "array": [ + { + "array": [ + { + "option": { + "defined": "Sign" + } + }, + 3 + ] + }, + 3 + ] + } + }, + { + "name": "state", + "type": { + "defined": "GameState" + } + } + ] + } + } + ], + "types": [ + { + "name": "Tile", + "type": { + "kind": "struct", + "fields": [ + { + "name": "row", + "type": "u8" + }, + { + "name": "column", + "type": "u8" + } + ] + } + }, + { + "name": "GameState", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Active" + }, + { + "name": "Tie" + }, + { + "name": "Won", + "fields": [ + { + "name": "winner", + "type": "publicKey" + } + ] + } + ] + } + }, + { + "name": "Sign", + "type": { + "kind": "enum", + "variants": [ + { + "name": "X" + }, + { + "name": "O" + } + ] + } + } + ], + "errors": [ + { + "code": 6000, + "name": "TileOutOfBounds" + }, + { + "code": 6001, + "name": "TileAlreadySet" + }, + { + "code": 6002, + "name": "GameAlreadyOver" + }, + { + "code": 6003, + "name": "NotPlayersTurn" + }, + { + "code": 6004, + "name": "GameAlreadyStarted" + } + ], + "metadata": { + "address": "4V6T2muqU1mHSg5XfK3SMgxZWQRSEUh72LzwGTLHGzsY" + } +} diff --git a/basics/anchorpy-main/tests/idls/typescript.json b/basics/anchorpy-main/tests/idls/typescript.json new file mode 100644 index 0000000..6aa06ab --- /dev/null +++ b/basics/anchorpy-main/tests/idls/typescript.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.0", + "name": "typescript", + "instructions": [ + { + "name": "initialize", + "accounts": [], + "args": [] + } + ] +} \ No newline at end of file From a7932b05fbaaf82222211ea6ff9f0656d462570e Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:32:00 +0530 Subject: [PATCH 12/19] Delete basics/anchorpy-main/tests/idls/as --- basics/anchorpy-main/tests/idls/as | 1 - 1 file changed, 1 deletion(-) delete mode 100644 basics/anchorpy-main/tests/idls/as diff --git a/basics/anchorpy-main/tests/idls/as b/basics/anchorpy-main/tests/idls/as deleted file mode 100644 index 8b13789..0000000 --- a/basics/anchorpy-main/tests/idls/as +++ /dev/null @@ -1 +0,0 @@ - From 5fc7d43cbcfa6e5b66873113f555feb0a73d63bd Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:32:23 +0530 Subject: [PATCH 13/19] Create aa --- basics/anchorpy-main/tests/unit/aa | 1 + 1 file changed, 1 insertion(+) create mode 100644 basics/anchorpy-main/tests/unit/aa diff --git a/basics/anchorpy-main/tests/unit/aa b/basics/anchorpy-main/tests/unit/aa new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/aa @@ -0,0 +1 @@ + From b81c4deba8530384d04c3ff9aad273ab0894369b Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:33:08 +0530 Subject: [PATCH 14/19] Add files via upload --- .../tests/unit/test_accounts_array.py | 30 +++++++ .../tests/unit/test_accounts_coder.py | 16 ++++ .../tests/unit/test_clientgen.py | 57 ++++++++++++++ .../tests/unit/test_event_parser.py | 32 ++++++++ basics/anchorpy-main/tests/unit/test_idl.py | 39 ++++++++++ .../tests/unit/test_instruction_coder.py | 21 +++++ .../tests/unit/test_transaction.py | 78 +++++++++++++++++++ 7 files changed, 273 insertions(+) create mode 100644 basics/anchorpy-main/tests/unit/test_accounts_array.py create mode 100644 basics/anchorpy-main/tests/unit/test_accounts_coder.py create mode 100644 basics/anchorpy-main/tests/unit/test_clientgen.py create mode 100644 basics/anchorpy-main/tests/unit/test_event_parser.py create mode 100644 basics/anchorpy-main/tests/unit/test_idl.py create mode 100644 basics/anchorpy-main/tests/unit/test_instruction_coder.py create mode 100644 basics/anchorpy-main/tests/unit/test_transaction.py diff --git a/basics/anchorpy-main/tests/unit/test_accounts_array.py b/basics/anchorpy-main/tests/unit/test_accounts_array.py new file mode 100644 index 0000000..97bf3e0 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_accounts_array.py @@ -0,0 +1,30 @@ +from pathlib import Path + +from anchorpy import Idl +from anchorpy.program.namespace.instruction import _accounts_array +from pytest import mark +from solana.transaction import AccountMeta +from solders.keypair import Keypair + + +@mark.unit +def test_accounts_array() -> None: + """Test accounts_array returns expected.""" + raw = Path("tests/idls/composite.json").read_text() + idl = Idl.from_json(raw) + dummy_a = Keypair() + dummy_b = Keypair() + comp_accounts = { + "foo": { + "dummy_a": dummy_a.pubkey(), + }, + "bar": { + "dummy_b": dummy_b.pubkey(), + }, + } + accounts_arg = idl.instructions[1].accounts + acc_arr = _accounts_array(comp_accounts, accounts_arg) + assert acc_arr == [ + AccountMeta(pubkey=dummy_a.pubkey(), is_signer=False, is_writable=True), + AccountMeta(pubkey=dummy_b.pubkey(), is_signer=False, is_writable=True), + ] diff --git a/basics/anchorpy-main/tests/unit/test_accounts_coder.py b/basics/anchorpy-main/tests/unit/test_accounts_coder.py new file mode 100644 index 0000000..906b4e8 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_accounts_coder.py @@ -0,0 +1,16 @@ +from pathlib import Path + +from anchorpy import AccountsCoder, Idl +from pytest import mark + + +@mark.unit +def test_accounts_coder() -> None: + """Test accounts coder.""" + raw = Path("tests/idls/basic_1.json").read_text() + idl = Idl.from_json(raw) + raw_acc_data = b"\xf6\x1c\x06W\xfb-2*\xd2\x04\x00\x00\x00\x00\x00\x00" + acc_coder = AccountsCoder(idl) + decoded = acc_coder.parse(raw_acc_data) + encoded = acc_coder.build(decoded) + assert encoded == raw_acc_data diff --git a/basics/anchorpy-main/tests/unit/test_clientgen.py b/basics/anchorpy-main/tests/unit/test_clientgen.py new file mode 100644 index 0000000..7938049 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_clientgen.py @@ -0,0 +1,57 @@ +from pathlib import Path + +from anchorpy import Idl +from anchorpy.clientgen.instructions import gen_accounts +from anchorpy.clientgen.types import gen_struct +from genpy import Suite + + +def test_gen_accounts() -> None: + path = Path("tests/idls/composite.json") + raw = path.read_text() + idl = Idl.from_json(raw) + accs = gen_accounts( + "CompositeUpdateAccounts", idl.instructions[1].accounts, gen_pdas=True + )[0] + suite = Suite(accs) + assert str(suite) == ( + " class CompositeUpdateAccounts(typing.TypedDict):" + "\n foo: FooNested" + "\n bar: BarNested" + "\n class FooNested(typing.TypedDict):" + "\n dummy_a: Pubkey" + "\n class BarNested(typing.TypedDict):" + "\n dummy_b: Pubkey" + "\n class FooNested(typing.TypedDict):" + "\n dummy_a: Pubkey" + ) + + +def test_empty_fields() -> None: + path = Path("tests/idls/switchboard_v2.mainnet.06022022.json") + raw = path.read_text() + idl = Idl.from_json(raw) + struct = gen_struct(idl, "AggregatorLockParams", []) + assert str(struct) == ( + "import typing" + "\nfrom dataclasses import dataclass" + "\nfrom construct import Container, Construct" + "\nfrom solders.pubkey import Pubkey" + "\nfrom anchorpy.borsh_extension import BorshPubkey" + "\nimport borsh_construct as borsh" + "\nclass AggregatorLockParamsJSON(typing.TypedDict):" + "\n pass" + "\n@dataclass" + "\nclass AggregatorLockParams():" + "\n layout: typing.ClassVar = borsh.CStruct()" + "\n @classmethod" + '\n def from_decoded(cls, obj: Container) -> "AggregatorLockParams":' + "\n return cls()" + "\n def to_encodable(self) -> dict[str, typing.Any]:" + "\n return {}" + "\n def to_json(self) -> AggregatorLockParamsJSON:" + "\n return {}" + "\n @classmethod" + '\n def from_json(cls, obj: AggregatorLockParamsJSON) -> "AggregatorLockParams":' + "\n return cls()" + ) diff --git a/basics/anchorpy-main/tests/unit/test_event_parser.py b/basics/anchorpy-main/tests/unit/test_event_parser.py new file mode 100644 index 0000000..54debcf --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_event_parser.py @@ -0,0 +1,32 @@ +from pathlib import Path + +from anchorpy import Event, EventParser, Idl, Program +from solders.pubkey import Pubkey + + +def test_event_parser() -> None: + path = Path("tests/idls/events.json") + raw = path.read_text() + idl = Idl.from_json(raw) + program = Program( + idl, Pubkey.from_string("2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy") + ) + logs = [ + "Program 2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy invoke [1]", + "Program log: Instruction: Initialize", + "Program data: YLjF84sCWpQFAAAAAAAAAAUAAABoZWxsbw==", + "Program 2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy consumed 1019 of 1400000 compute units", + "Program 2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy success", + ] + parser = EventParser(program.program_id, program.coder) + evts = [] + parser.parse_logs(logs, lambda evt: evts.append(evt)) + assert len(evts) == 1 + events_coder = program.coder.events + event_cls = events_coder.layouts["MyEvent"].datacls # type: ignore + expected_data = event_cls( + data=5, + label="hello", + ) + expected_event = Event(name="MyEvent", data=expected_data) + assert evts[0] == expected_event diff --git a/basics/anchorpy-main/tests/unit/test_idl.py b/basics/anchorpy-main/tests/unit/test_idl.py new file mode 100644 index 0000000..f8e08aa --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_idl.py @@ -0,0 +1,39 @@ +from pathlib import Path + +from anchorpy import Idl, Program +from solders.pubkey import Pubkey + + +def test_idls() -> None: + idls = [] + programs = [] + for path in Path("tests/idls/").iterdir(): + raw = path.read_text() + idl = Idl.from_json(raw) + idls.append(idl) + if "spl_token" not in str(path): + program = Program(idl, Pubkey.default()) + programs.append(program) + assert idls + + +def test_jet_enum() -> None: + path = Path("tests/idls/jet.json") + raw = path.read_text() + idl = Idl.from_json(raw) + program = Program(idl, Pubkey.default()) + expired_err = program.type["CacheInvalidError"].Expired + assert expired_err(msg="hi").msg == "hi" + + +def test_switchboard_tuple() -> None: + path = Path("tests/idls/switchboard.json") + raw = path.read_text() + idl = Idl.from_json(raw) + program = Program(idl, Pubkey.default()) # noqa: F841 + + +def test_clientgen_example() -> None: + path = Path("tests/idls/clientgen_example_program.json") + raw = path.read_text() + Idl.from_json(raw) diff --git a/basics/anchorpy-main/tests/unit/test_instruction_coder.py b/basics/anchorpy-main/tests/unit/test_instruction_coder.py new file mode 100644 index 0000000..a06e2d6 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_instruction_coder.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from anchorpy import Idl, InstructionCoder +from anchorpy.program.common import _to_instruction +from anchorpy.program.context import _check_args_length +from pytest import mark + + +@mark.unit +def test_instruction_coder() -> None: + """Test InstructionCoder behaves as expected.""" + raw = Path("tests/idls/basic_1.json").read_text() + idl = Idl.from_json(raw) + idl_ix = idl.instructions[0] + args = (1234,) + _check_args_length(idl_ix, args) + ix = _to_instruction(idl_ix, args) + coder = InstructionCoder(idl) + encoded = coder.build(ix) + assert encoded == b"\xaf\xafm\x1f\r\x98\x9b\xed\xd2\x04\x00\x00\x00\x00\x00\x00" + assert coder.parse(encoded) == ix diff --git a/basics/anchorpy-main/tests/unit/test_transaction.py b/basics/anchorpy-main/tests/unit/test_transaction.py new file mode 100644 index 0000000..136d732 --- /dev/null +++ b/basics/anchorpy-main/tests/unit/test_transaction.py @@ -0,0 +1,78 @@ +from anchorpy import Coder, Idl +from anchorpy.program.context import Context +from anchorpy.program.namespace.instruction import _InstructionFn +from anchorpy.program.namespace.transaction import _build_transaction_fn +from pytest import fixture +from solders.hash import Hash +from solders.instruction import Instruction +from solders.keypair import Keypair +from solders.pubkey import Pubkey + +DEFAULT_PUBKEY = Pubkey.default() + + +def _make_ix(data: bytes) -> Instruction: + return Instruction( + accounts=[], + program_id=DEFAULT_PUBKEY, + data=data, + ) + + +@fixture +def pre_ix() -> Instruction: + return _make_ix(b"pre") + + +@fixture +def post_ix() -> Instruction: + return _make_ix(b"post") + + +@fixture +def idl() -> Idl: + raw = """{ + "version": "0.0.0", + "name": "basic_0", + "instructions": [ + { + "name": "initialize", + "accounts": [], + "args": [] + } + ] + }""" + return Idl.from_json(raw) + + +@fixture +def coder(idl: Idl) -> Coder: + return Coder(idl) + + +def test_pre_instructions(coder: Coder, idl: Idl, pre_ix: Instruction) -> None: + coder.instruction.encode + ix_item = _InstructionFn( + idl.instructions[0], coder.instruction.build, DEFAULT_PUBKEY + ) + tx_item = _build_transaction_fn(idl.instructions[0], ix_item) + tx = tx_item( + ctx=Context(pre_instructions=[pre_ix]), + payer=Keypair(), + blockhash=Hash.default(), + ) + assert len(tx.message.instructions) == 2 + + +def test_post_instructions(coder: Coder, idl: Idl, post_ix: Instruction) -> None: + coder.instruction.encode + ix_item = _InstructionFn( + idl.instructions[0], coder.instruction.build, DEFAULT_PUBKEY + ) + tx_item = _build_transaction_fn(idl.instructions[0], ix_item) + tx = tx_item( + ctx=Context(post_instructions=[post_ix]), + payer=Keypair(), + blockhash=Hash.default(), + ) + assert len(tx.message.instructions) == 2 From b61dd8254d58a1aed812e5940e6aaf5365f41bf8 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:33:26 +0530 Subject: [PATCH 15/19] Delete basics/anchorpy-main/tests/unit/aa --- basics/anchorpy-main/tests/unit/aa | 1 - 1 file changed, 1 deletion(-) delete mode 100644 basics/anchorpy-main/tests/unit/aa diff --git a/basics/anchorpy-main/tests/unit/aa b/basics/anchorpy-main/tests/unit/aa deleted file mode 100644 index 8b13789..0000000 --- a/basics/anchorpy-main/tests/unit/aa +++ /dev/null @@ -1 +0,0 @@ - From 267800a9649ec116855d1b11d2156f501b7b62f3 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:33:59 +0530 Subject: [PATCH 16/19] Create aq --- basics/anchorpy-main/tests/client-gen/aq | 1 + 1 file changed, 1 insertion(+) create mode 100644 basics/anchorpy-main/tests/client-gen/aq diff --git a/basics/anchorpy-main/tests/client-gen/aq b/basics/anchorpy-main/tests/client-gen/aq new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/aq @@ -0,0 +1 @@ + From 3bd06b1adf6b24b53e9e6d67b8a1ed27bd1c6dd4 Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:35:22 +0530 Subject: [PATCH 17/19] Add files via upload --- .../tests/client-gen/__init__.py | 0 .../client-gen/example-program/Anchor.toml | 12 + .../client-gen/example-program/Cargo.lock | 1562 +++++++++++++++++ .../client-gen/example-program/Cargo.toml | 4 + .../programs/example-program/Cargo.toml | 19 + .../programs/example-program/Xargo.toml | 2 + .../programs/example-program/src/lib.rs | 351 ++++ .../example_program_gen/__init__.py | 0 .../example_program_gen/accounts/__init__.py | 4 + .../accounts/base_account.py | 87 + .../accounts/my_account.py | 79 + .../example_program_gen/accounts/state.py | 259 +++ .../example_program_gen/accounts/state2.py | 82 + .../example_program_gen/errors/__init__.py | 29 + .../example_program_gen/errors/anchor.py | 590 +++++++ .../example_program_gen/errors/custom.py | 34 + .../instructions/__init__.py | 17 + .../instructions/cause_error.py | 18 + .../increment_state_when_present.py | 33 + .../instructions/init_my_account.py | 72 + .../instructions/initialize.py | 32 + .../instructions/initialize_with_values.py | 132 ++ .../instructions/initialize_with_values2.py | 45 + .../example_program_gen/program_id.py | 3 + .../example_program_gen/types/__init__.py | 7 + .../example_program_gen/types/bar_struct.py | 33 + .../example_program_gen/types/foo_enum.py | 370 ++++ .../example_program_gen/types/foo_struct.py | 97 + .../tests/client-gen/test_client_gen.py | 444 +++++ .../tests/client-gen/test_functional.py | 357 ++++ .../tests/client-gen/token/__init__.py | 0 .../client-gen/token/accounts/__init__.py | 3 + .../client-gen/token/accounts/account.py | 133 ++ .../tests/client-gen/token/accounts/mint.py | 118 ++ .../client-gen/token/accounts/multisig.py | 100 ++ .../tests/client-gen/token/errors/__init__.py | 29 + .../tests/client-gen/token/errors/anchor.py | 590 +++++++ .../tests/client-gen/token/errors/custom.py | 239 +++ .../client-gen/token/instructions/__init__.py | 60 + .../token/instructions/amount_to_ui_amount.py | 38 + .../client-gen/token/instructions/approve.py | 42 + .../token/instructions/approve_checked.py | 46 + .../client-gen/token/instructions/burn.py | 42 + .../token/instructions/burn_checked.py | 44 + .../token/instructions/close_account.py | 29 + .../token/instructions/freeze_account.py | 29 + .../instructions/get_account_data_size.py | 25 + .../token/instructions/initialize_account.py | 31 + .../token/instructions/initialize_account2.py | 43 + .../token/instructions/initialize_account3.py | 41 + .../initialize_immutable_owner.py | 25 + .../token/instructions/initialize_mint.py | 49 + .../token/instructions/initialize_mint2.py | 47 + .../token/instructions/initialize_multisig.py | 40 + .../instructions/initialize_multisig2.py | 40 + .../client-gen/token/instructions/mint_to.py | 42 + .../token/instructions/mint_to_checked.py | 44 + .../client-gen/token/instructions/revoke.py | 27 + .../token/instructions/set_authority.py | 49 + .../token/instructions/sync_native.py | 25 + .../token/instructions/thaw_account.py | 29 + .../client-gen/token/instructions/transfer.py | 42 + .../token/instructions/transfer_checked.py | 46 + .../token/instructions/ui_amount_to_amount.py | 38 + .../tests/client-gen/token/program_id.py | 3 + .../tests/client-gen/token/types/__init__.py | 5 + .../client-gen/token/types/account_state.py | 105 ++ .../client-gen/token/types/authority_type.py | 134 ++ 68 files changed, 7246 insertions(+) create mode 100644 basics/anchorpy-main/tests/client-gen/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/Anchor.toml create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/Cargo.lock create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/Cargo.toml create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Cargo.toml create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Xargo.toml create mode 100644 basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/src/lib.rs create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/base_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/my_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state2.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/errors/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/errors/anchor.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/errors/custom.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/cause_error.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/increment_state_when_present.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/init_my_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values2.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/program_id.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/types/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/types/bar_struct.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_enum.py create mode 100644 basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_struct.py create mode 100644 basics/anchorpy-main/tests/client-gen/test_client_gen.py create mode 100644 basics/anchorpy-main/tests/client-gen/test_functional.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/accounts/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/accounts/account.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/accounts/mint.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/accounts/multisig.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/errors/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/errors/anchor.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/errors/custom.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/amount_to_ui_amount.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/approve.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/approve_checked.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/burn.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/burn_checked.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/close_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/freeze_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/get_account_data_size.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account2.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account3.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_immutable_owner.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint2.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig2.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/mint_to.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/mint_to_checked.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/revoke.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/set_authority.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/sync_native.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/thaw_account.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/transfer.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/transfer_checked.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/instructions/ui_amount_to_amount.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/program_id.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/types/__init__.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/types/account_state.py create mode 100644 basics/anchorpy-main/tests/client-gen/token/types/authority_type.py diff --git a/basics/anchorpy-main/tests/client-gen/__init__.py b/basics/anchorpy-main/tests/client-gen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/tests/client-gen/example-program/Anchor.toml b/basics/anchorpy-main/tests/client-gen/example-program/Anchor.toml new file mode 100644 index 0000000..d6d5c12 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/Anchor.toml @@ -0,0 +1,12 @@ +[programs.localnet] +example_program = "3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8" + +[registry] +url = "https://anchor.projectserum.com" + +[provider] +cluster = "localnet" +wallet = "~/.config/solana/id.json" + +[features] +seeds = true diff --git a/basics/anchorpy-main/tests/client-gen/example-program/Cargo.lock b/basics/anchorpy-main/tests/client-gen/example-program/Cargo.lock new file mode 100644 index 0000000..17ca9f0 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/Cargo.lock @@ -0,0 +1,1562 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7d535e1381be3de2c0716c0a1c1e32ad9df1042cddcf7bc18d743569e53319" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "regex", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3bcd731f21048a032be27c7791701120e44f3f6371358fc4261a7f716283d29" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1be64a48e395fe00b8217287f226078be2cf32dae42fdf8a885b997945c3d28" +dependencies = [ + "anchor-syn", + "proc-macro2", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ea6713d1938c0da03656ff8a693b17dc0396da66d1ba320557f07e86eca0d4" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401f11efb3644285685f8339829a9786d43ed7490bb1699f33c478d04d5a582" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-interface" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6700a6f5c888a9c33fe8afc0c64fd8575fa28d05446037306d0f96102ae4480" +dependencies = [ + "anchor-syn", + "anyhow", + "heck", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad769993b5266714e8939e47fbdede90e5c030333c7522d99a4d4748cf26712" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-attribute-state" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e677fae4a016a554acdd0e3b7f178d3acafaa7e7ffac6b8690cf4e171f1c116" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340beef6809d1c3fcc7ae219153d981e95a8a277ff31985bd7050e32645dc9a8" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "anchor-lang" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662ceafe667448ee4199a4be2ee83b6bb76da28566eee5cea05f96ab38255af8" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-interface", + "anchor-attribute-program", + "anchor-attribute-state", + "anchor-derive-accounts", + "arrayref", + "base64 0.13.0", + "bincode", + "borsh", + "bytemuck", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-syn" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0418bcb5daac3b8cb1b60d8fdb1d468ca36f5509f31fb51179326fae1028fdcc" +dependencies = [ + "anyhow", + "bs58 0.3.1", + "heck", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "serde", + "serde_json", + "sha2 0.9.9", + "syn 1.0.102", + "thiserror", +] + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake3" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.5", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.102", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "example-program" +version = "0.1.0" +dependencies = [ + "anchor-lang", +] + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", + "version_check", + "yansi", +] + +[[package]] +name = "quote" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "serde_json" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.5", + "keccak", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "solana-frozen-abi" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a100b7fa8198c20354eb7256c0d9789107d8a62280221f3efe15f7c9dc4cec" +dependencies = [ + "ahash", + "blake3", + "block-buffer 0.9.0", + "bs58 0.4.0", + "bv", + "byteorder", + "cc", + "either", + "generic-array", + "getrandom 0.1.16", + "hashbrown 0.12.3", + "im", + "lazy_static", + "log", + "memmap2", + "once_cell", + "rand_core 0.6.4", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.6", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f527f44601b35dd67d11bc72f2f7512976a466f9304ef574b87dac83ced8a42" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.102", +] + +[[package]] +name = "solana-program" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ad5f48743ce505f6139a07e20aecdc689def12da7230fed661c2073ab97df8" +dependencies = [ + "base64 0.13.0", + "bincode", + "bitflags", + "blake3", + "borsh", + "borsh-derive", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.7", + "itertools", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "log", + "memoffset 0.6.5", + "num-derive", + "num-traits", + "parking_lot", + "rand", + "rand_chacha", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.6", + "sha3", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbc3ab3070c090e1a18fd5a0a07d729d0db2bc8524414dc3e16504286d38049" +dependencies = [ + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.102", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2", + "rand", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.102", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.102", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] diff --git a/basics/anchorpy-main/tests/client-gen/example-program/Cargo.toml b/basics/anchorpy-main/tests/client-gen/example-program/Cargo.toml new file mode 100644 index 0000000..a60de98 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "programs/*" +] diff --git a/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Cargo.toml b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Cargo.toml new file mode 100644 index 0000000..c08202b --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "example-program" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "example_program" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.26.0" diff --git a/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Xargo.toml b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Xargo.toml new file mode 100644 index 0000000..475fb71 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/src/lib.rs b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/src/lib.rs new file mode 100644 index 0000000..0875609 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example-program/programs/example-program/src/lib.rs @@ -0,0 +1,351 @@ +use std::str::FromStr; + +use anchor_lang::prelude::*; + +declare_id!("3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8"); + +pub const MY_SEED: [u8; 2] = *b"hi"; +pub const MY_SEED_STR: &str = "hi"; +pub const MY_SEED_U8: u8 = 1; +pub const MY_SEED_U32: u32 = 2; +pub const MY_SEED_U64: u64 = 3; + +#[program] +pub mod example_program { + use super::*; + + pub fn initialize(ctx: Context) -> Result<()> { + ctx.accounts.state.set_inner(State::default()); + Ok(()) + } + + pub fn initialize_with_values( + ctx: Context, + bool_field: bool, + u8_field: u8, + i8_field: i8, + u16_field: u16, + i16_field: i16, + u32_field: u32, + i32_field: i32, + f32_field: f32, + u64_field: u64, + i64_field: i64, + f64_field: f64, + u128_field: u128, + i128_field: i128, + bytes_field: Vec, + string_field: String, + pubkey_field: Pubkey, + vec_field: Vec, + vec_struct_field: Vec, + option_field: Option, + option_struct_field: Option, + struct_field: FooStruct, + array_field: [bool; 3], + enum_field_1: FooEnum, + enum_field_2: FooEnum, + enum_field_3: FooEnum, + enum_field_4: FooEnum, + ) -> Result<()> { + ctx.accounts.state.set_inner(State { + bool_field, + u8_field, + i8_field, + u16_field, + i16_field, + u32_field, + i32_field, + f32_field, + u64_field, + i64_field, + f64_field, + u128_field, + i128_field, + bytes_field, + string_field, + pubkey_field, + vec_field, + vec_struct_field, + option_field, + option_struct_field, + struct_field, + array_field, + enum_field_1, + enum_field_2, + enum_field_3, + enum_field_4, + }); + + Ok(()) + } + + // a separate instruction due to initialize_with_values having too many arguments + // https://github.com/solana-labs/solana/issues/23978 + pub fn initialize_with_values2( + ctx: Context, + vec_of_option: Vec>, + ) -> Result<()> { + ctx.accounts.state.set_inner(State2 { vec_of_option }); + Ok(()) + } + + pub fn cause_error(_ctx: Context) -> Result<()> { + return Err(error!(ErrorCode::SomeError)); + } + + pub fn init_my_account(ctx: Context, _seed_a: u8) -> Result<()> { + ctx.accounts.account.data = 1337; + Ok(()) + } + + pub fn increment_state_when_present(ctx: Context) -> Result<()> { + if let Some(state) = ctx.accounts.first_state.as_mut() { + state.u8_field += 1; + } + Ok(()) + } +} + +#[derive(Accounts)] +pub struct InitMyAccount<'info> { + base: Account<'info, BaseAccount>, + /// CHECK: shut up + base2: AccountInfo<'info>, + #[account( + seeds = [ + "another-seed".as_bytes(), + b"test".as_ref(), + MY_SEED.as_ref(), + MY_SEED_STR.as_bytes(), + MY_SEED_U8.to_le_bytes().as_ref(), + &MY_SEED_U32.to_le_bytes(), + &MY_SEED_U64.to_le_bytes(), + ], + bump, + )] + account: Account<'info, MyAccount>, + nested: Nested<'info>, + system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct Nested<'info> { + #[account( + seeds = [ + "nested-seed".as_bytes(), + b"test".as_ref(), + MY_SEED.as_ref(), + MY_SEED_STR.as_bytes(), + MY_SEED_U8.to_le_bytes().as_ref(), + &MY_SEED_U32.to_le_bytes(), + &MY_SEED_U64.to_le_bytes(), + ], + bump, + )] + /// CHECK: Not needed + account_nested: AccountInfo<'info>, +} + +#[account] +pub struct MyAccount { + data: u64, +} + +#[account] +pub struct BaseAccount { + base_data: u64, + base_data_key: Pubkey, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub enum FooEnum { + Unnamed(bool, u8, BarStruct), + UnnamedSingle(BarStruct), + Named { + bool_field: bool, + u8_field: u8, + nested: BarStruct, + }, + Struct(BarStruct), + OptionStruct(Option), + VecStruct(Vec), + NoFields, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct BarStruct { + some_field: bool, + other_field: u8, +} + +impl Default for BarStruct { + fn default() -> Self { + return BarStruct { + some_field: true, + other_field: 10, + }; + } +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct FooStruct { + field1: u8, + field2: u16, + nested: BarStruct, + vec_nested: Vec, + option_nested: Option, + enum_field: FooEnum, +} + +impl Default for FooStruct { + fn default() -> Self { + return FooStruct { + field1: 123, + field2: 999, + nested: BarStruct::default(), + vec_nested: vec![BarStruct::default()], + option_nested: Some(BarStruct::default()), + enum_field: FooEnum::Named { + bool_field: true, + u8_field: 15, + nested: BarStruct::default(), + }, + }; + } +} + +#[account] +pub struct State { + bool_field: bool, + u8_field: u8, + i8_field: i8, + u16_field: u16, + i16_field: i16, + u32_field: u32, + i32_field: i32, + f32_field: f32, + u64_field: u64, + i64_field: i64, + f64_field: f64, + u128_field: u128, + i128_field: i128, + bytes_field: Vec, + string_field: String, + pubkey_field: Pubkey, + vec_field: Vec, + vec_struct_field: Vec, + option_field: Option, + option_struct_field: Option, + struct_field: FooStruct, + array_field: [bool; 3], + enum_field_1: FooEnum, + enum_field_2: FooEnum, + enum_field_3: FooEnum, + enum_field_4: FooEnum, +} + +impl Default for State { + fn default() -> Self { + // some arbitrary default values + return State { + bool_field: true, + u8_field: 234, + i8_field: -123, + u16_field: 62345, + i16_field: -31234, + u32_field: 1234567891, + i32_field: -1234567891, + f32_field: 123456.5, + u64_field: u64::MAX / 2 + 10, + i64_field: i64::MIN / 2 - 10, + f64_field: 1234567891.345, + u128_field: u128::MAX / 2 + 10, + i128_field: i128::MIN / 2 - 10, + bytes_field: vec![1, 2, 255, 254], + string_field: String::from("hello"), + pubkey_field: Pubkey::from_str("EPZP2wrcRtMxrAPJCXVEQaYD9eH7fH7h12YqKDcd4aS7").unwrap(), + vec_field: vec![1, 2, 100, 1000, u64::MAX], + vec_struct_field: vec![FooStruct::default()], + option_field: None, + option_struct_field: Some(FooStruct::default()), + struct_field: FooStruct::default(), + array_field: [true, false, true], + enum_field_1: FooEnum::Unnamed(false, 10, BarStruct::default()), + enum_field_2: FooEnum::Named { + bool_field: true, + u8_field: 20, + nested: BarStruct::default(), + }, + enum_field_3: FooEnum::Struct(BarStruct::default()), + enum_field_4: FooEnum::NoFields, + }; + } +} + +#[account] +pub struct State2 { + vec_of_option: Vec>, +} +impl Default for State2 { + fn default() -> Self { + return State2 { + vec_of_option: vec![None, Some(10)], + }; + } +} + +#[derive(Accounts)] +pub struct NestedAccounts<'info> { + clock: Sysvar<'info, Clock>, + rent: Sysvar<'info, Rent>, +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account( + init, + space = 8 + 1000, // TODO: use exact space required + payer = payer, + )] + state: Account<'info, State>, + + nested: NestedAccounts<'info>, + + #[account(mut)] + payer: Signer<'info>, + system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct Initialize2<'info> { + #[account( + init, + space = 8 + 1000, // TODO: use exact space required + payer = payer, + )] + state: Account<'info, State2>, + + #[account(mut)] + payer: Signer<'info>, + system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct IncrementStateWhenPresent<'info> { + #[account(mut)] + first_state: Option>, + second_state: Account<'info, State2>, + system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct CauseError {} + +#[error_code] +pub enum ErrorCode { + #[msg("Example error.")] + SomeError, + #[msg("Another error.")] + OtherError, +} diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/__init__.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/__init__.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/__init__.py new file mode 100644 index 0000000..69f5a39 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/__init__.py @@ -0,0 +1,4 @@ +from .my_account import MyAccount, MyAccountJSON +from .base_account import BaseAccount, BaseAccountJSON +from .state import State, StateJSON +from .state2 import State2, State2JSON diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/base_account.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/base_account.py new file mode 100644 index 0000000..b3978ca --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/base_account.py @@ -0,0 +1,87 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey +from ..program_id import PROGRAM_ID + + +class BaseAccountJSON(typing.TypedDict): + base_data: int + base_data_key: str + + +@dataclass +class BaseAccount: + discriminator: typing.ClassVar = b"\x10Z\x82\xf2\x9f\n\xe8\x85" + layout: typing.ClassVar = borsh.CStruct( + "base_data" / borsh.U64, "base_data_key" / BorshPubkey + ) + base_data: int + base_data_key: Pubkey + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["BaseAccount"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["BaseAccount"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["BaseAccount"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "BaseAccount": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = BaseAccount.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + base_data=dec.base_data, + base_data_key=dec.base_data_key, + ) + + def to_json(self) -> BaseAccountJSON: + return { + "base_data": self.base_data, + "base_data_key": str(self.base_data_key), + } + + @classmethod + def from_json(cls, obj: BaseAccountJSON) -> "BaseAccount": + return cls( + base_data=obj["base_data"], + base_data_key=Pubkey.from_string(obj["base_data_key"]), + ) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/my_account.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/my_account.py new file mode 100644 index 0000000..49c922b --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/my_account.py @@ -0,0 +1,79 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from ..program_id import PROGRAM_ID + + +class MyAccountJSON(typing.TypedDict): + data: int + + +@dataclass +class MyAccount: + discriminator: typing.ClassVar = b"\xf6\x1c\x06W\xfb-2*" + layout: typing.ClassVar = borsh.CStruct("data" / borsh.U64) + data: int + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["MyAccount"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["MyAccount"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["MyAccount"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "MyAccount": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = MyAccount.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + data=dec.data, + ) + + def to_json(self) -> MyAccountJSON: + return { + "data": self.data, + } + + @classmethod + def from_json(cls, obj: MyAccountJSON) -> "MyAccount": + return cls( + data=obj["data"], + ) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state.py new file mode 100644 index 0000000..1c0f39f --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state.py @@ -0,0 +1,259 @@ +import typing +from dataclasses import dataclass +from construct import Construct +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey +from ..program_id import PROGRAM_ID +from .. import types + + +class StateJSON(typing.TypedDict): + bool_field: bool + u8_field: int + i8_field: int + u16_field: int + i16_field: int + u32_field: int + i32_field: int + f32_field: float + u64_field: int + i64_field: int + f64_field: float + u128_field: int + i128_field: int + bytes_field: list[int] + string_field: str + pubkey_field: str + vec_field: list[int] + vec_struct_field: list[types.foo_struct.FooStructJSON] + option_field: typing.Optional[bool] + option_struct_field: typing.Optional[types.foo_struct.FooStructJSON] + struct_field: types.foo_struct.FooStructJSON + array_field: list[bool] + enum_field1: types.foo_enum.FooEnumJSON + enum_field2: types.foo_enum.FooEnumJSON + enum_field3: types.foo_enum.FooEnumJSON + enum_field4: types.foo_enum.FooEnumJSON + + +@dataclass +class State: + discriminator: typing.ClassVar = b"\xd8\x92k^hK\xb6\xb1" + layout: typing.ClassVar = borsh.CStruct( + "bool_field" / borsh.Bool, + "u8_field" / borsh.U8, + "i8_field" / borsh.I8, + "u16_field" / borsh.U16, + "i16_field" / borsh.I16, + "u32_field" / borsh.U32, + "i32_field" / borsh.I32, + "f32_field" / borsh.F32, + "u64_field" / borsh.U64, + "i64_field" / borsh.I64, + "f64_field" / borsh.F64, + "u128_field" / borsh.U128, + "i128_field" / borsh.I128, + "bytes_field" / borsh.Bytes, + "string_field" / borsh.String, + "pubkey_field" / BorshPubkey, + "vec_field" / borsh.Vec(typing.cast(Construct, borsh.U64)), + "vec_struct_field" + / borsh.Vec(typing.cast(Construct, types.foo_struct.FooStruct.layout)), + "option_field" / borsh.Option(borsh.Bool), + "option_struct_field" / borsh.Option(types.foo_struct.FooStruct.layout), + "struct_field" / types.foo_struct.FooStruct.layout, + "array_field" / borsh.Bool[3], + "enum_field1" / types.foo_enum.layout, + "enum_field2" / types.foo_enum.layout, + "enum_field3" / types.foo_enum.layout, + "enum_field4" / types.foo_enum.layout, + ) + bool_field: bool + u8_field: int + i8_field: int + u16_field: int + i16_field: int + u32_field: int + i32_field: int + f32_field: float + u64_field: int + i64_field: int + f64_field: float + u128_field: int + i128_field: int + bytes_field: bytes + string_field: str + pubkey_field: Pubkey + vec_field: list[int] + vec_struct_field: list[types.foo_struct.FooStruct] + option_field: typing.Optional[bool] + option_struct_field: typing.Optional[types.foo_struct.FooStruct] + struct_field: types.foo_struct.FooStruct + array_field: list[bool] + enum_field1: types.foo_enum.FooEnumKind + enum_field2: types.foo_enum.FooEnumKind + enum_field3: types.foo_enum.FooEnumKind + enum_field4: types.foo_enum.FooEnumKind + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["State"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["State"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["State"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "State": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = State.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + bool_field=dec.bool_field, + u8_field=dec.u8_field, + i8_field=dec.i8_field, + u16_field=dec.u16_field, + i16_field=dec.i16_field, + u32_field=dec.u32_field, + i32_field=dec.i32_field, + f32_field=dec.f32_field, + u64_field=dec.u64_field, + i64_field=dec.i64_field, + f64_field=dec.f64_field, + u128_field=dec.u128_field, + i128_field=dec.i128_field, + bytes_field=dec.bytes_field, + string_field=dec.string_field, + pubkey_field=dec.pubkey_field, + vec_field=dec.vec_field, + vec_struct_field=list( + map( + lambda item: types.foo_struct.FooStruct.from_decoded(item), + dec.vec_struct_field, + ) + ), + option_field=dec.option_field, + option_struct_field=( + None + if dec.option_struct_field is None + else types.foo_struct.FooStruct.from_decoded(dec.option_struct_field) + ), + struct_field=types.foo_struct.FooStruct.from_decoded(dec.struct_field), + array_field=dec.array_field, + enum_field1=types.foo_enum.from_decoded(dec.enum_field1), + enum_field2=types.foo_enum.from_decoded(dec.enum_field2), + enum_field3=types.foo_enum.from_decoded(dec.enum_field3), + enum_field4=types.foo_enum.from_decoded(dec.enum_field4), + ) + + def to_json(self) -> StateJSON: + return { + "bool_field": self.bool_field, + "u8_field": self.u8_field, + "i8_field": self.i8_field, + "u16_field": self.u16_field, + "i16_field": self.i16_field, + "u32_field": self.u32_field, + "i32_field": self.i32_field, + "f32_field": self.f32_field, + "u64_field": self.u64_field, + "i64_field": self.i64_field, + "f64_field": self.f64_field, + "u128_field": self.u128_field, + "i128_field": self.i128_field, + "bytes_field": list(self.bytes_field), + "string_field": self.string_field, + "pubkey_field": str(self.pubkey_field), + "vec_field": self.vec_field, + "vec_struct_field": list( + map(lambda item: item.to_json(), self.vec_struct_field) + ), + "option_field": self.option_field, + "option_struct_field": ( + None + if self.option_struct_field is None + else self.option_struct_field.to_json() + ), + "struct_field": self.struct_field.to_json(), + "array_field": self.array_field, + "enum_field1": self.enum_field1.to_json(), + "enum_field2": self.enum_field2.to_json(), + "enum_field3": self.enum_field3.to_json(), + "enum_field4": self.enum_field4.to_json(), + } + + @classmethod + def from_json(cls, obj: StateJSON) -> "State": + return cls( + bool_field=obj["bool_field"], + u8_field=obj["u8_field"], + i8_field=obj["i8_field"], + u16_field=obj["u16_field"], + i16_field=obj["i16_field"], + u32_field=obj["u32_field"], + i32_field=obj["i32_field"], + f32_field=obj["f32_field"], + u64_field=obj["u64_field"], + i64_field=obj["i64_field"], + f64_field=obj["f64_field"], + u128_field=obj["u128_field"], + i128_field=obj["i128_field"], + bytes_field=bytes(obj["bytes_field"]), + string_field=obj["string_field"], + pubkey_field=Pubkey.from_string(obj["pubkey_field"]), + vec_field=obj["vec_field"], + vec_struct_field=list( + map( + lambda item: types.foo_struct.FooStruct.from_json(item), + obj["vec_struct_field"], + ) + ), + option_field=obj["option_field"], + option_struct_field=( + None + if obj["option_struct_field"] is None + else types.foo_struct.FooStruct.from_json(obj["option_struct_field"]) + ), + struct_field=types.foo_struct.FooStruct.from_json(obj["struct_field"]), + array_field=obj["array_field"], + enum_field1=types.foo_enum.from_json(obj["enum_field1"]), + enum_field2=types.foo_enum.from_json(obj["enum_field2"]), + enum_field3=types.foo_enum.from_json(obj["enum_field3"]), + enum_field4=types.foo_enum.from_json(obj["enum_field4"]), + ) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state2.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state2.py new file mode 100644 index 0000000..2dd9ab1 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/accounts/state2.py @@ -0,0 +1,82 @@ +import typing +from dataclasses import dataclass +from construct import Construct +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from ..program_id import PROGRAM_ID + + +class State2JSON(typing.TypedDict): + vec_of_option: list[typing.Optional[int]] + + +@dataclass +class State2: + discriminator: typing.ClassVar = b"ja\xff\xa1\xfa\xcd\xb9\xc0" + layout: typing.ClassVar = borsh.CStruct( + "vec_of_option" / borsh.Vec(typing.cast(Construct, borsh.Option(borsh.U64))) + ) + vec_of_option: list[typing.Optional[int]] + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["State2"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["State2"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["State2"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "State2": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = State2.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + vec_of_option=dec.vec_of_option, + ) + + def to_json(self) -> State2JSON: + return { + "vec_of_option": self.vec_of_option, + } + + @classmethod + def from_json(cls, obj: State2JSON) -> "State2": + return cls( + vec_of_option=obj["vec_of_option"], + ) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/__init__.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/__init__.py new file mode 100644 index 0000000..421993d --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/__init__.py @@ -0,0 +1,29 @@ +import typing +import re +from solders.transaction_status import ( + InstructionErrorCustom, + TransactionErrorInstructionError, +) +from solana.rpc.core import RPCException +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from anchorpy.error import extract_code_and_logs +from ..program_id import PROGRAM_ID +from . import anchor +from . import custom + + +def from_code(code: int) -> typing.Union[custom.CustomError, anchor.AnchorError, None]: + return custom.from_code(code) if code >= 6000 else anchor.from_code(code) + + +error_re = re.compile(r"Program (\w+) failed: custom program error: (\w+)") + + +def from_tx_error( + error: RPCException, +) -> typing.Union[anchor.AnchorError, custom.CustomError, None]: + err_info = error.args[0] + extracted = extract_code_and_logs(err_info, PROGRAM_ID) + if extracted is None: + return None + return from_code(extracted[0]) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/anchor.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/anchor.py new file mode 100644 index 0000000..3f266ef --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/anchor.py @@ -0,0 +1,590 @@ +import typing +from anchorpy.error import ProgramError + + +class InstructionMissing(ProgramError): + def __init__(self): + super().__init__(100, "8 byte instruction identifier not provided") + + code = 100 + name = "InstructionMissing" + msg = "8 byte instruction identifier not provided" + + +class InstructionFallbackNotFound(ProgramError): + def __init__(self): + super().__init__(101, "Fallback functions are not supported") + + code = 101 + name = "InstructionFallbackNotFound" + msg = "Fallback functions are not supported" + + +class InstructionDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(102, "The program could not deserialize the given instruction") + + code = 102 + name = "InstructionDidNotDeserialize" + msg = "The program could not deserialize the given instruction" + + +class InstructionDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(103, "The program could not serialize the given instruction") + + code = 103 + name = "InstructionDidNotSerialize" + msg = "The program could not serialize the given instruction" + + +class IdlInstructionStub(ProgramError): + def __init__(self): + super().__init__(1000, "The program was compiled without idl instructions") + + code = 1000 + name = "IdlInstructionStub" + msg = "The program was compiled without idl instructions" + + +class IdlInstructionInvalidProgram(ProgramError): + def __init__(self): + super().__init__( + 1001, "The transaction was given an invalid program for the IDL instruction" + ) + + code = 1001 + name = "IdlInstructionInvalidProgram" + msg = "The transaction was given an invalid program for the IDL instruction" + + +class ConstraintMut(ProgramError): + def __init__(self): + super().__init__(2000, "A mut constraint was violated") + + code = 2000 + name = "ConstraintMut" + msg = "A mut constraint was violated" + + +class ConstraintHasOne(ProgramError): + def __init__(self): + super().__init__(2001, "A has_one constraint was violated") + + code = 2001 + name = "ConstraintHasOne" + msg = "A has_one constraint was violated" + + +class ConstraintSigner(ProgramError): + def __init__(self): + super().__init__(2002, "A signer constraint was violated") + + code = 2002 + name = "ConstraintSigner" + msg = "A signer constraint was violated" + + +class ConstraintRaw(ProgramError): + def __init__(self): + super().__init__(2003, "A raw constraint was violated") + + code = 2003 + name = "ConstraintRaw" + msg = "A raw constraint was violated" + + +class ConstraintOwner(ProgramError): + def __init__(self): + super().__init__(2004, "An owner constraint was violated") + + code = 2004 + name = "ConstraintOwner" + msg = "An owner constraint was violated" + + +class ConstraintRentExempt(ProgramError): + def __init__(self): + super().__init__(2005, "A rent exempt constraint was violated") + + code = 2005 + name = "ConstraintRentExempt" + msg = "A rent exempt constraint was violated" + + +class ConstraintSeeds(ProgramError): + def __init__(self): + super().__init__(2006, "A seeds constraint was violated") + + code = 2006 + name = "ConstraintSeeds" + msg = "A seeds constraint was violated" + + +class ConstraintExecutable(ProgramError): + def __init__(self): + super().__init__(2007, "An executable constraint was violated") + + code = 2007 + name = "ConstraintExecutable" + msg = "An executable constraint was violated" + + +class ConstraintState(ProgramError): + def __init__(self): + super().__init__(2008, "A state constraint was violated") + + code = 2008 + name = "ConstraintState" + msg = "A state constraint was violated" + + +class ConstraintAssociated(ProgramError): + def __init__(self): + super().__init__(2009, "An associated constraint was violated") + + code = 2009 + name = "ConstraintAssociated" + msg = "An associated constraint was violated" + + +class ConstraintAssociatedInit(ProgramError): + def __init__(self): + super().__init__(2010, "An associated init constraint was violated") + + code = 2010 + name = "ConstraintAssociatedInit" + msg = "An associated init constraint was violated" + + +class ConstraintClose(ProgramError): + def __init__(self): + super().__init__(2011, "A close constraint was violated") + + code = 2011 + name = "ConstraintClose" + msg = "A close constraint was violated" + + +class ConstraintAddress(ProgramError): + def __init__(self): + super().__init__(2012, "An address constraint was violated") + + code = 2012 + name = "ConstraintAddress" + msg = "An address constraint was violated" + + +class ConstraintZero(ProgramError): + def __init__(self): + super().__init__(2013, "Expected zero account discriminant") + + code = 2013 + name = "ConstraintZero" + msg = "Expected zero account discriminant" + + +class ConstraintTokenMint(ProgramError): + def __init__(self): + super().__init__(2014, "A token mint constraint was violated") + + code = 2014 + name = "ConstraintTokenMint" + msg = "A token mint constraint was violated" + + +class ConstraintTokenOwner(ProgramError): + def __init__(self): + super().__init__(2015, "A token owner constraint was violated") + + code = 2015 + name = "ConstraintTokenOwner" + msg = "A token owner constraint was violated" + + +class ConstraintMintMintAuthority(ProgramError): + def __init__(self): + super().__init__(2016, "A mint mint authority constraint was violated") + + code = 2016 + name = "ConstraintMintMintAuthority" + msg = "A mint mint authority constraint was violated" + + +class ConstraintMintFreezeAuthority(ProgramError): + def __init__(self): + super().__init__(2017, "A mint freeze authority constraint was violated") + + code = 2017 + name = "ConstraintMintFreezeAuthority" + msg = "A mint freeze authority constraint was violated" + + +class ConstraintMintDecimals(ProgramError): + def __init__(self): + super().__init__(2018, "A mint decimals constraint was violated") + + code = 2018 + name = "ConstraintMintDecimals" + msg = "A mint decimals constraint was violated" + + +class ConstraintSpace(ProgramError): + def __init__(self): + super().__init__(2019, "A space constraint was violated") + + code = 2019 + name = "ConstraintSpace" + msg = "A space constraint was violated" + + +class RequireViolated(ProgramError): + def __init__(self): + super().__init__(2500, "A require expression was violated") + + code = 2500 + name = "RequireViolated" + msg = "A require expression was violated" + + +class RequireEqViolated(ProgramError): + def __init__(self): + super().__init__(2501, "A require_eq expression was violated") + + code = 2501 + name = "RequireEqViolated" + msg = "A require_eq expression was violated" + + +class RequireKeysEqViolated(ProgramError): + def __init__(self): + super().__init__(2502, "A require_keys_eq expression was violated") + + code = 2502 + name = "RequireKeysEqViolated" + msg = "A require_keys_eq expression was violated" + + +class RequireNeqViolated(ProgramError): + def __init__(self): + super().__init__(2503, "A require_neq expression was violated") + + code = 2503 + name = "RequireNeqViolated" + msg = "A require_neq expression was violated" + + +class RequireKeysNeqViolated(ProgramError): + def __init__(self): + super().__init__(2504, "A require_keys_neq expression was violated") + + code = 2504 + name = "RequireKeysNeqViolated" + msg = "A require_keys_neq expression was violated" + + +class RequireGtViolated(ProgramError): + def __init__(self): + super().__init__(2505, "A require_gt expression was violated") + + code = 2505 + name = "RequireGtViolated" + msg = "A require_gt expression was violated" + + +class RequireGteViolated(ProgramError): + def __init__(self): + super().__init__(2506, "A require_gte expression was violated") + + code = 2506 + name = "RequireGteViolated" + msg = "A require_gte expression was violated" + + +class AccountDiscriminatorAlreadySet(ProgramError): + def __init__(self): + super().__init__( + 3000, "The account discriminator was already set on this account" + ) + + code = 3000 + name = "AccountDiscriminatorAlreadySet" + msg = "The account discriminator was already set on this account" + + +class AccountDiscriminatorNotFound(ProgramError): + def __init__(self): + super().__init__(3001, "No 8 byte discriminator was found on the account") + + code = 3001 + name = "AccountDiscriminatorNotFound" + msg = "No 8 byte discriminator was found on the account" + + +class AccountDiscriminatorMismatch(ProgramError): + def __init__(self): + super().__init__(3002, "8 byte discriminator did not match what was expected") + + code = 3002 + name = "AccountDiscriminatorMismatch" + msg = "8 byte discriminator did not match what was expected" + + +class AccountDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(3003, "Failed to deserialize the account") + + code = 3003 + name = "AccountDidNotDeserialize" + msg = "Failed to deserialize the account" + + +class AccountDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(3004, "Failed to serialize the account") + + code = 3004 + name = "AccountDidNotSerialize" + msg = "Failed to serialize the account" + + +class AccountNotEnoughKeys(ProgramError): + def __init__(self): + super().__init__(3005, "Not enough account keys given to the instruction") + + code = 3005 + name = "AccountNotEnoughKeys" + msg = "Not enough account keys given to the instruction" + + +class AccountNotMutable(ProgramError): + def __init__(self): + super().__init__(3006, "The given account is not mutable") + + code = 3006 + name = "AccountNotMutable" + msg = "The given account is not mutable" + + +class AccountOwnedByWrongProgram(ProgramError): + def __init__(self): + super().__init__( + 3007, "The given account is owned by a different program than expected" + ) + + code = 3007 + name = "AccountOwnedByWrongProgram" + msg = "The given account is owned by a different program than expected" + + +class InvalidProgramId(ProgramError): + def __init__(self): + super().__init__(3008, "Program ID was not as expected") + + code = 3008 + name = "InvalidProgramId" + msg = "Program ID was not as expected" + + +class InvalidProgramExecutable(ProgramError): + def __init__(self): + super().__init__(3009, "Program account is not executable") + + code = 3009 + name = "InvalidProgramExecutable" + msg = "Program account is not executable" + + +class AccountNotSigner(ProgramError): + def __init__(self): + super().__init__(3010, "The given account did not sign") + + code = 3010 + name = "AccountNotSigner" + msg = "The given account did not sign" + + +class AccountNotSystemOwned(ProgramError): + def __init__(self): + super().__init__(3011, "The given account is not owned by the system program") + + code = 3011 + name = "AccountNotSystemOwned" + msg = "The given account is not owned by the system program" + + +class AccountNotInitialized(ProgramError): + def __init__(self): + super().__init__( + 3012, "The program expected this account to be already initialized" + ) + + code = 3012 + name = "AccountNotInitialized" + msg = "The program expected this account to be already initialized" + + +class AccountNotProgramData(ProgramError): + def __init__(self): + super().__init__(3013, "The given account is not a program data account") + + code = 3013 + name = "AccountNotProgramData" + msg = "The given account is not a program data account" + + +class AccountNotAssociatedTokenAccount(ProgramError): + def __init__(self): + super().__init__(3014, "The given account is not the associated token account") + + code = 3014 + name = "AccountNotAssociatedTokenAccount" + msg = "The given account is not the associated token account" + + +class AccountSysvarMismatch(ProgramError): + def __init__(self): + super().__init__( + 3015, "The given public key does not match the required sysvar" + ) + + code = 3015 + name = "AccountSysvarMismatch" + msg = "The given public key does not match the required sysvar" + + +class StateInvalidAddress(ProgramError): + def __init__(self): + super().__init__( + 4000, "The given state account does not have the correct address" + ) + + code = 4000 + name = "StateInvalidAddress" + msg = "The given state account does not have the correct address" + + +class Deprecated(ProgramError): + def __init__(self): + super().__init__( + 5000, "The API being used is deprecated and should no longer be used" + ) + + code = 5000 + name = "Deprecated" + msg = "The API being used is deprecated and should no longer be used" + + +AnchorError = typing.Union[ + InstructionMissing, + InstructionFallbackNotFound, + InstructionDidNotDeserialize, + InstructionDidNotSerialize, + IdlInstructionStub, + IdlInstructionInvalidProgram, + ConstraintMut, + ConstraintHasOne, + ConstraintSigner, + ConstraintRaw, + ConstraintOwner, + ConstraintRentExempt, + ConstraintSeeds, + ConstraintExecutable, + ConstraintState, + ConstraintAssociated, + ConstraintAssociatedInit, + ConstraintClose, + ConstraintAddress, + ConstraintZero, + ConstraintTokenMint, + ConstraintTokenOwner, + ConstraintMintMintAuthority, + ConstraintMintFreezeAuthority, + ConstraintMintDecimals, + ConstraintSpace, + RequireViolated, + RequireEqViolated, + RequireKeysEqViolated, + RequireNeqViolated, + RequireKeysNeqViolated, + RequireGtViolated, + RequireGteViolated, + AccountDiscriminatorAlreadySet, + AccountDiscriminatorNotFound, + AccountDiscriminatorMismatch, + AccountDidNotDeserialize, + AccountDidNotSerialize, + AccountNotEnoughKeys, + AccountNotMutable, + AccountOwnedByWrongProgram, + InvalidProgramId, + InvalidProgramExecutable, + AccountNotSigner, + AccountNotSystemOwned, + AccountNotInitialized, + AccountNotProgramData, + AccountNotAssociatedTokenAccount, + AccountSysvarMismatch, + StateInvalidAddress, + Deprecated, +] +ANCHOR_ERROR_MAP: dict[int, AnchorError] = { + 100: InstructionMissing(), + 101: InstructionFallbackNotFound(), + 102: InstructionDidNotDeserialize(), + 103: InstructionDidNotSerialize(), + 1000: IdlInstructionStub(), + 1001: IdlInstructionInvalidProgram(), + 2000: ConstraintMut(), + 2001: ConstraintHasOne(), + 2002: ConstraintSigner(), + 2003: ConstraintRaw(), + 2004: ConstraintOwner(), + 2005: ConstraintRentExempt(), + 2006: ConstraintSeeds(), + 2007: ConstraintExecutable(), + 2008: ConstraintState(), + 2009: ConstraintAssociated(), + 2010: ConstraintAssociatedInit(), + 2011: ConstraintClose(), + 2012: ConstraintAddress(), + 2013: ConstraintZero(), + 2014: ConstraintTokenMint(), + 2015: ConstraintTokenOwner(), + 2016: ConstraintMintMintAuthority(), + 2017: ConstraintMintFreezeAuthority(), + 2018: ConstraintMintDecimals(), + 2019: ConstraintSpace(), + 2500: RequireViolated(), + 2501: RequireEqViolated(), + 2502: RequireKeysEqViolated(), + 2503: RequireNeqViolated(), + 2504: RequireKeysNeqViolated(), + 2505: RequireGtViolated(), + 2506: RequireGteViolated(), + 3000: AccountDiscriminatorAlreadySet(), + 3001: AccountDiscriminatorNotFound(), + 3002: AccountDiscriminatorMismatch(), + 3003: AccountDidNotDeserialize(), + 3004: AccountDidNotSerialize(), + 3005: AccountNotEnoughKeys(), + 3006: AccountNotMutable(), + 3007: AccountOwnedByWrongProgram(), + 3008: InvalidProgramId(), + 3009: InvalidProgramExecutable(), + 3010: AccountNotSigner(), + 3011: AccountNotSystemOwned(), + 3012: AccountNotInitialized(), + 3013: AccountNotProgramData(), + 3014: AccountNotAssociatedTokenAccount(), + 3015: AccountSysvarMismatch(), + 4000: StateInvalidAddress(), + 5000: Deprecated(), +} + + +def from_code(code: int) -> typing.Optional[AnchorError]: + maybe_err = ANCHOR_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/custom.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/custom.py new file mode 100644 index 0000000..f92f321 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/errors/custom.py @@ -0,0 +1,34 @@ +import typing +from anchorpy.error import ProgramError + + +class SomeError(ProgramError): + def __init__(self) -> None: + super().__init__(6000, "Example error.") + + code = 6000 + name = "SomeError" + msg = "Example error." + + +class OtherError(ProgramError): + def __init__(self) -> None: + super().__init__(6001, "Another error.") + + code = 6001 + name = "OtherError" + msg = "Another error." + + +CustomError = typing.Union[SomeError, OtherError] +CUSTOM_ERROR_MAP: dict[int, CustomError] = { + 6000: SomeError(), + 6001: OtherError(), +} + + +def from_code(code: int) -> typing.Optional[CustomError]: + maybe_err = CUSTOM_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/__init__.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/__init__.py new file mode 100644 index 0000000..dee442f --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/__init__.py @@ -0,0 +1,17 @@ +from .initialize import initialize, InitializeAccounts +from .initialize_with_values import ( + initialize_with_values, + InitializeWithValuesArgs, + InitializeWithValuesAccounts, +) +from .initialize_with_values2 import ( + initialize_with_values2, + InitializeWithValues2Args, + InitializeWithValues2Accounts, +) +from .cause_error import cause_error +from .init_my_account import init_my_account, InitMyAccountArgs, InitMyAccountAccounts +from .increment_state_when_present import ( + increment_state_when_present, + IncrementStateWhenPresentAccounts, +) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/cause_error.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/cause_error.py new file mode 100644 index 0000000..4bbd6f3 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/cause_error.py @@ -0,0 +1,18 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +def cause_error( + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"Ch%\x11\x02\x9bD\x11" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/increment_state_when_present.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/increment_state_when_present.py new file mode 100644 index 0000000..f7a41af --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/increment_state_when_present.py @@ -0,0 +1,33 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class IncrementStateWhenPresentAccounts(typing.TypedDict): + first_state: typing.Optional[Pubkey] + second_state: Pubkey + + +def increment_state_when_present( + accounts: IncrementStateWhenPresentAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["first_state"], is_signer=False, is_writable=True) + if accounts["first_state"] + else AccountMeta(pubkey=program_id, is_signer=False, is_writable=False), + AccountMeta( + pubkey=accounts["second_state"], is_signer=False, is_writable=False + ), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xf1!\xdd(\xa3Jy%" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/init_my_account.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/init_my_account.py new file mode 100644 index 0000000..063a597 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/init_my_account.py @@ -0,0 +1,72 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitMyAccountArgs(typing.TypedDict): + seed_a: int + + +layout = borsh.CStruct("seed_a" / borsh.U8) +NESTED_NESTED_ACCOUNT_NESTED = Pubkey.find_program_address( + seeds=[ + b"nested-seed", + b"test", + b"hi", + b"hi", + b"\x01", + b"\x02\x00\x00\x00", + b"\x03\x00\x00\x00\x00\x00\x00\x00", + ], + program_id=PROGRAM_ID, +)[0] +INIT_MY_ACCOUNT_ACCOUNTS_ACCOUNT = Pubkey.find_program_address( + seeds=[ + b"another-seed", + b"test", + b"hi", + b"hi", + b"\x01", + b"\x02\x00\x00\x00", + b"\x03\x00\x00\x00\x00\x00\x00\x00", + ], + program_id=PROGRAM_ID, +)[0] + + +class InitMyAccountAccounts(typing.TypedDict): + base: Pubkey + base2: Pubkey + + +def init_my_account( + args: InitMyAccountArgs, + accounts: InitMyAccountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["base"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["base2"], is_signer=False, is_writable=False), + AccountMeta( + pubkey=INIT_MY_ACCOUNT_ACCOUNTS_ACCOUNT, is_signer=False, is_writable=False + ), + AccountMeta( + pubkey=NESTED_NESTED_ACCOUNT_NESTED, is_signer=False, is_writable=False + ), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b" ;\x05\xcd^E\xe3z" + encoded_args = layout.build( + { + "seed_a": args["seed_a"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize.py new file mode 100644 index 0000000..45b0cb7 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize.py @@ -0,0 +1,32 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.sysvar import RENT, CLOCK +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class InitializeAccounts(typing.TypedDict): + state: Pubkey + payer: Pubkey + + +def initialize( + accounts: InitializeAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["state"], is_signer=True, is_writable=True), + AccountMeta(pubkey=CLOCK, is_signer=False, is_writable=False), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["payer"], is_signer=True, is_writable=True), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xaf\xafm\x1f\r\x98\x9b\xed" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values.py new file mode 100644 index 0000000..ccec19f --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values.py @@ -0,0 +1,132 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.sysvar import RENT, CLOCK +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey +from construct import Construct +import borsh_construct as borsh +from .. import types +from ..program_id import PROGRAM_ID + + +class InitializeWithValuesArgs(typing.TypedDict): + bool_field: bool + u8_field: int + i8_field: int + u16_field: int + i16_field: int + u32_field: int + i32_field: int + f32_field: float + u64_field: int + i64_field: int + f64_field: float + u128_field: int + i128_field: int + bytes_field: bytes + string_field: str + pubkey_field: Pubkey + vec_field: list[int] + vec_struct_field: list[types.foo_struct.FooStruct] + option_field: typing.Optional[bool] + option_struct_field: typing.Optional[types.foo_struct.FooStruct] + struct_field: types.foo_struct.FooStruct + array_field: list[bool] + enum_field1: types.foo_enum.FooEnumKind + enum_field2: types.foo_enum.FooEnumKind + enum_field3: types.foo_enum.FooEnumKind + enum_field4: types.foo_enum.FooEnumKind + + +layout = borsh.CStruct( + "bool_field" / borsh.Bool, + "u8_field" / borsh.U8, + "i8_field" / borsh.I8, + "u16_field" / borsh.U16, + "i16_field" / borsh.I16, + "u32_field" / borsh.U32, + "i32_field" / borsh.I32, + "f32_field" / borsh.F32, + "u64_field" / borsh.U64, + "i64_field" / borsh.I64, + "f64_field" / borsh.F64, + "u128_field" / borsh.U128, + "i128_field" / borsh.I128, + "bytes_field" / borsh.Bytes, + "string_field" / borsh.String, + "pubkey_field" / BorshPubkey, + "vec_field" / borsh.Vec(typing.cast(Construct, borsh.U64)), + "vec_struct_field" + / borsh.Vec(typing.cast(Construct, types.foo_struct.FooStruct.layout)), + "option_field" / borsh.Option(borsh.Bool), + "option_struct_field" / borsh.Option(types.foo_struct.FooStruct.layout), + "struct_field" / types.foo_struct.FooStruct.layout, + "array_field" / borsh.Bool[3], + "enum_field1" / types.foo_enum.layout, + "enum_field2" / types.foo_enum.layout, + "enum_field3" / types.foo_enum.layout, + "enum_field4" / types.foo_enum.layout, +) + + +class InitializeWithValuesAccounts(typing.TypedDict): + state: Pubkey + payer: Pubkey + + +def initialize_with_values( + args: InitializeWithValuesArgs, + accounts: InitializeWithValuesAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["state"], is_signer=True, is_writable=True), + AccountMeta(pubkey=CLOCK, is_signer=False, is_writable=False), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["payer"], is_signer=True, is_writable=True), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xdcI\x08\xd5\xb2E\xb5\x8d" + encoded_args = layout.build( + { + "bool_field": args["bool_field"], + "u8_field": args["u8_field"], + "i8_field": args["i8_field"], + "u16_field": args["u16_field"], + "i16_field": args["i16_field"], + "u32_field": args["u32_field"], + "i32_field": args["i32_field"], + "f32_field": args["f32_field"], + "u64_field": args["u64_field"], + "i64_field": args["i64_field"], + "f64_field": args["f64_field"], + "u128_field": args["u128_field"], + "i128_field": args["i128_field"], + "bytes_field": args["bytes_field"], + "string_field": args["string_field"], + "pubkey_field": args["pubkey_field"], + "vec_field": args["vec_field"], + "vec_struct_field": list( + map(lambda item: item.to_encodable(), args["vec_struct_field"]) + ), + "option_field": args["option_field"], + "option_struct_field": ( + None + if args["option_struct_field"] is None + else args["option_struct_field"].to_encodable() + ), + "struct_field": args["struct_field"].to_encodable(), + "array_field": args["array_field"], + "enum_field1": args["enum_field1"].to_encodable(), + "enum_field2": args["enum_field2"].to_encodable(), + "enum_field3": args["enum_field3"].to_encodable(), + "enum_field4": args["enum_field4"].to_encodable(), + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values2.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values2.py new file mode 100644 index 0000000..6e56be8 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/instructions/initialize_with_values2.py @@ -0,0 +1,45 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.instruction import Instruction, AccountMeta +from construct import Construct +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeWithValues2Args(typing.TypedDict): + vec_of_option: list[typing.Optional[int]] + + +layout = borsh.CStruct( + "vec_of_option" / borsh.Vec(typing.cast(Construct, borsh.Option(borsh.U64))) +) + + +class InitializeWithValues2Accounts(typing.TypedDict): + state: Pubkey + payer: Pubkey + + +def initialize_with_values2( + args: InitializeWithValues2Args, + accounts: InitializeWithValues2Accounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["state"], is_signer=True, is_writable=True), + AccountMeta(pubkey=accounts["payer"], is_signer=True, is_writable=True), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xf8\xbe\x15a\xef\x94'\xb5" + encoded_args = layout.build( + { + "vec_of_option": args["vec_of_option"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/program_id.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/program_id.py new file mode 100644 index 0000000..649b93e --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/program_id.py @@ -0,0 +1,3 @@ +from solders.pubkey import Pubkey + +PROGRAM_ID = Pubkey.from_string("3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8") diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/types/__init__.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/__init__.py new file mode 100644 index 0000000..a843328 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/__init__.py @@ -0,0 +1,7 @@ +import typing +from . import bar_struct +from .bar_struct import BarStruct, BarStructJSON +from . import foo_struct +from .foo_struct import FooStruct, FooStructJSON +from . import foo_enum +from .foo_enum import FooEnumKind, FooEnumJSON diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/types/bar_struct.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/bar_struct.py new file mode 100644 index 0000000..9ab95a7 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/bar_struct.py @@ -0,0 +1,33 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from construct import Container +import borsh_construct as borsh + + +class BarStructJSON(typing.TypedDict): + some_field: bool + other_field: int + + +@dataclass +class BarStruct: + layout: typing.ClassVar = borsh.CStruct( + "some_field" / borsh.Bool, "other_field" / borsh.U8 + ) + some_field: bool + other_field: int + + @classmethod + def from_decoded(cls, obj: Container) -> "BarStruct": + return cls(some_field=obj.some_field, other_field=obj.other_field) + + def to_encodable(self) -> dict[str, typing.Any]: + return {"some_field": self.some_field, "other_field": self.other_field} + + def to_json(self) -> BarStructJSON: + return {"some_field": self.some_field, "other_field": self.other_field} + + @classmethod + def from_json(cls, obj: BarStructJSON) -> "BarStruct": + return cls(some_field=obj["some_field"], other_field=obj["other_field"]) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_enum.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_enum.py new file mode 100644 index 0000000..b56185f --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_enum.py @@ -0,0 +1,370 @@ +from __future__ import annotations +from . import bar_struct +import typing +from dataclasses import dataclass +from construct import Construct +from anchorpy.borsh_extension import EnumForCodegen +import borsh_construct as borsh + +UnnamedJSONValue = tuple[bool, int, bar_struct.BarStructJSON] +UnnamedSingleJSONValue = tuple[bar_struct.BarStructJSON] + + +class NamedJSONValue(typing.TypedDict): + bool_field: bool + u8_field: int + nested: bar_struct.BarStructJSON + + +StructJSONValue = tuple[bar_struct.BarStructJSON] +OptionStructJSONValue = tuple[typing.Optional[bar_struct.BarStructJSON]] +VecStructJSONValue = tuple[list[bar_struct.BarStructJSON]] +UnnamedValue = tuple[bool, int, bar_struct.BarStruct] +UnnamedSingleValue = tuple[bar_struct.BarStruct] + + +class NamedValue(typing.TypedDict): + bool_field: bool + u8_field: int + nested: bar_struct.BarStruct + + +StructValue = tuple[bar_struct.BarStruct] +OptionStructValue = tuple[typing.Optional[bar_struct.BarStruct]] +VecStructValue = tuple[list[bar_struct.BarStruct]] + + +class UnnamedJSON(typing.TypedDict): + value: UnnamedJSONValue + kind: typing.Literal["Unnamed"] + + +class UnnamedSingleJSON(typing.TypedDict): + value: UnnamedSingleJSONValue + kind: typing.Literal["UnnamedSingle"] + + +class NamedJSON(typing.TypedDict): + value: NamedJSONValue + kind: typing.Literal["Named"] + + +class StructJSON(typing.TypedDict): + value: StructJSONValue + kind: typing.Literal["Struct"] + + +class OptionStructJSON(typing.TypedDict): + value: OptionStructJSONValue + kind: typing.Literal["OptionStruct"] + + +class VecStructJSON(typing.TypedDict): + value: VecStructJSONValue + kind: typing.Literal["VecStruct"] + + +class NoFieldsJSON(typing.TypedDict): + kind: typing.Literal["NoFields"] + + +@dataclass +class Unnamed: + discriminator: typing.ClassVar = 0 + kind: typing.ClassVar = "Unnamed" + value: UnnamedValue + + def to_json(self) -> UnnamedJSON: + return UnnamedJSON( + kind="Unnamed", + value=( + self.value[0], + self.value[1], + self.value[2].to_json(), + ), + ) + + def to_encodable(self) -> dict: + return { + "Unnamed": { + "item_0": self.value[0], + "item_1": self.value[1], + "item_2": self.value[2].to_encodable(), + }, + } + + +@dataclass +class UnnamedSingle: + discriminator: typing.ClassVar = 1 + kind: typing.ClassVar = "UnnamedSingle" + value: UnnamedSingleValue + + def to_json(self) -> UnnamedSingleJSON: + return UnnamedSingleJSON( + kind="UnnamedSingle", + value=(self.value[0].to_json(),), + ) + + def to_encodable(self) -> dict: + return { + "UnnamedSingle": { + "item_0": self.value[0].to_encodable(), + }, + } + + +@dataclass +class Named: + discriminator: typing.ClassVar = 2 + kind: typing.ClassVar = "Named" + value: NamedValue + + def to_json(self) -> NamedJSON: + return NamedJSON( + kind="Named", + value={ + "bool_field": self.value["bool_field"], + "u8_field": self.value["u8_field"], + "nested": self.value["nested"].to_json(), + }, + ) + + def to_encodable(self) -> dict: + return { + "Named": { + "bool_field": self.value["bool_field"], + "u8_field": self.value["u8_field"], + "nested": self.value["nested"].to_encodable(), + }, + } + + +@dataclass +class Struct: + discriminator: typing.ClassVar = 3 + kind: typing.ClassVar = "Struct" + value: StructValue + + def to_json(self) -> StructJSON: + return StructJSON( + kind="Struct", + value=(self.value[0].to_json(),), + ) + + def to_encodable(self) -> dict: + return { + "Struct": { + "item_0": self.value[0].to_encodable(), + }, + } + + +@dataclass +class OptionStruct: + discriminator: typing.ClassVar = 4 + kind: typing.ClassVar = "OptionStruct" + value: OptionStructValue + + def to_json(self) -> OptionStructJSON: + return OptionStructJSON( + kind="OptionStruct", + value=((None if self.value[0] is None else self.value[0].to_json()),), + ) + + def to_encodable(self) -> dict: + return { + "OptionStruct": { + "item_0": ( + None if self.value[0] is None else self.value[0].to_encodable() + ), + }, + } + + +@dataclass +class VecStruct: + discriminator: typing.ClassVar = 5 + kind: typing.ClassVar = "VecStruct" + value: VecStructValue + + def to_json(self) -> VecStructJSON: + return VecStructJSON( + kind="VecStruct", + value=(list(map(lambda item: item.to_json(), self.value[0])),), + ) + + def to_encodable(self) -> dict: + return { + "VecStruct": { + "item_0": list(map(lambda item: item.to_encodable(), self.value[0])), + }, + } + + +@dataclass +class NoFields: + discriminator: typing.ClassVar = 6 + kind: typing.ClassVar = "NoFields" + + @classmethod + def to_json(cls) -> NoFieldsJSON: + return NoFieldsJSON( + kind="NoFields", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "NoFields": {}, + } + + +FooEnumKind = typing.Union[ + Unnamed, UnnamedSingle, Named, Struct, OptionStruct, VecStruct, NoFields +] +FooEnumJSON = typing.Union[ + UnnamedJSON, + UnnamedSingleJSON, + NamedJSON, + StructJSON, + OptionStructJSON, + VecStructJSON, + NoFieldsJSON, +] + + +def from_decoded(obj: dict) -> FooEnumKind: + if not isinstance(obj, dict): + raise ValueError("Invalid enum object") + if "Unnamed" in obj: + val = obj["Unnamed"] + return Unnamed( + ( + val["item_0"], + val["item_1"], + bar_struct.BarStruct.from_decoded(val["item_2"]), + ) + ) + if "UnnamedSingle" in obj: + val = obj["UnnamedSingle"] + return UnnamedSingle((bar_struct.BarStruct.from_decoded(val["item_0"]),)) + if "Named" in obj: + val = obj["Named"] + return Named( + NamedValue( + bool_field=val["bool_field"], + u8_field=val["u8_field"], + nested=bar_struct.BarStruct.from_decoded(val["nested"]), + ) + ) + if "Struct" in obj: + val = obj["Struct"] + return Struct((bar_struct.BarStruct.from_decoded(val["item_0"]),)) + if "OptionStruct" in obj: + val = obj["OptionStruct"] + return OptionStruct( + ( + ( + None + if val["item_0"] is None + else bar_struct.BarStruct.from_decoded(val["item_0"]) + ), + ) + ) + if "VecStruct" in obj: + val = obj["VecStruct"] + return VecStruct( + ( + list( + map( + lambda item: bar_struct.BarStruct.from_decoded(item), + val["item_0"], + ) + ), + ) + ) + if "NoFields" in obj: + return NoFields() + raise ValueError("Invalid enum object") + + +def from_json(obj: FooEnumJSON) -> FooEnumKind: + if obj["kind"] == "Unnamed": + unnamed_json_value = typing.cast(UnnamedJSONValue, obj["value"]) + return Unnamed( + ( + unnamed_json_value[0], + unnamed_json_value[1], + bar_struct.BarStruct.from_json(unnamed_json_value[2]), + ) + ) + if obj["kind"] == "UnnamedSingle": + unnamed_single_json_value = typing.cast(UnnamedSingleJSONValue, obj["value"]) + return UnnamedSingle( + (bar_struct.BarStruct.from_json(unnamed_single_json_value[0]),) + ) + if obj["kind"] == "Named": + named_json_value = typing.cast(NamedJSONValue, obj["value"]) + return Named( + NamedValue( + bool_field=named_json_value["bool_field"], + u8_field=named_json_value["u8_field"], + nested=bar_struct.BarStruct.from_json(named_json_value["nested"]), + ) + ) + if obj["kind"] == "Struct": + struct_json_value = typing.cast(StructJSONValue, obj["value"]) + return Struct((bar_struct.BarStruct.from_json(struct_json_value[0]),)) + if obj["kind"] == "OptionStruct": + option_struct_json_value = typing.cast(OptionStructJSONValue, obj["value"]) + return OptionStruct( + ( + ( + None + if option_struct_json_value[0] is None + else bar_struct.BarStruct.from_json(option_struct_json_value[0]) + ), + ) + ) + if obj["kind"] == "VecStruct": + vec_struct_json_value = typing.cast(VecStructJSONValue, obj["value"]) + return VecStruct( + ( + list( + map( + lambda item: bar_struct.BarStruct.from_json(item), + vec_struct_json_value[0], + ) + ), + ) + ) + if obj["kind"] == "NoFields": + return NoFields() + kind = obj["kind"] + raise ValueError(f"Unrecognized enum kind: {kind}") + + +layout = EnumForCodegen( + "Unnamed" + / borsh.CStruct( + "item_0" / borsh.Bool, + "item_1" / borsh.U8, + "item_2" / bar_struct.BarStruct.layout, + ), + "UnnamedSingle" / borsh.CStruct("item_0" / bar_struct.BarStruct.layout), + "Named" + / borsh.CStruct( + "bool_field" / borsh.Bool, + "u8_field" / borsh.U8, + "nested" / bar_struct.BarStruct.layout, + ), + "Struct" / borsh.CStruct("item_0" / bar_struct.BarStruct.layout), + "OptionStruct" + / borsh.CStruct("item_0" / borsh.Option(bar_struct.BarStruct.layout)), + "VecStruct" + / borsh.CStruct( + "item_0" / borsh.Vec(typing.cast(Construct, bar_struct.BarStruct.layout)) + ), + "NoFields" / borsh.CStruct(), +) diff --git a/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_struct.py b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_struct.py new file mode 100644 index 0000000..7db49d8 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/example_program_gen/types/foo_struct.py @@ -0,0 +1,97 @@ +from __future__ import annotations +from . import bar_struct, foo_enum +import typing +from dataclasses import dataclass +from construct import Container, Construct +import borsh_construct as borsh + + +class FooStructJSON(typing.TypedDict): + field1: int + field2: int + nested: bar_struct.BarStructJSON + vec_nested: list[bar_struct.BarStructJSON] + option_nested: typing.Optional[bar_struct.BarStructJSON] + enum_field: foo_enum.FooEnumJSON + + +@dataclass +class FooStruct: + layout: typing.ClassVar = borsh.CStruct( + "field1" / borsh.U8, + "field2" / borsh.U16, + "nested" / bar_struct.BarStruct.layout, + "vec_nested" / borsh.Vec(typing.cast(Construct, bar_struct.BarStruct.layout)), + "option_nested" / borsh.Option(bar_struct.BarStruct.layout), + "enum_field" / foo_enum.layout, + ) + field1: int + field2: int + nested: bar_struct.BarStruct + vec_nested: list[bar_struct.BarStruct] + option_nested: typing.Optional[bar_struct.BarStruct] + enum_field: foo_enum.FooEnumKind + + @classmethod + def from_decoded(cls, obj: Container) -> "FooStruct": + return cls( + field1=obj.field1, + field2=obj.field2, + nested=bar_struct.BarStruct.from_decoded(obj.nested), + vec_nested=list( + map( + lambda item: bar_struct.BarStruct.from_decoded(item), obj.vec_nested + ) + ), + option_nested=( + None + if obj.option_nested is None + else bar_struct.BarStruct.from_decoded(obj.option_nested) + ), + enum_field=foo_enum.from_decoded(obj.enum_field), + ) + + def to_encodable(self) -> dict[str, typing.Any]: + return { + "field1": self.field1, + "field2": self.field2, + "nested": self.nested.to_encodable(), + "vec_nested": list(map(lambda item: item.to_encodable(), self.vec_nested)), + "option_nested": ( + None + if self.option_nested is None + else self.option_nested.to_encodable() + ), + "enum_field": self.enum_field.to_encodable(), + } + + def to_json(self) -> FooStructJSON: + return { + "field1": self.field1, + "field2": self.field2, + "nested": self.nested.to_json(), + "vec_nested": list(map(lambda item: item.to_json(), self.vec_nested)), + "option_nested": ( + None if self.option_nested is None else self.option_nested.to_json() + ), + "enum_field": self.enum_field.to_json(), + } + + @classmethod + def from_json(cls, obj: FooStructJSON) -> "FooStruct": + return cls( + field1=obj["field1"], + field2=obj["field2"], + nested=bar_struct.BarStruct.from_json(obj["nested"]), + vec_nested=list( + map( + lambda item: bar_struct.BarStruct.from_json(item), obj["vec_nested"] + ) + ), + option_nested=( + None + if obj["option_nested"] is None + else bar_struct.BarStruct.from_json(obj["option_nested"]) + ), + enum_field=foo_enum.from_json(obj["enum_field"]), + ) diff --git a/basics/anchorpy-main/tests/client-gen/test_client_gen.py b/basics/anchorpy-main/tests/client-gen/test_client_gen.py new file mode 100644 index 0000000..03f2c3e --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/test_client_gen.py @@ -0,0 +1,444 @@ +import asyncio +import json +from pathlib import Path +from typing import AsyncGenerator, List + +from anchorpy import Provider, Wallet +from anchorpy.pytest_plugin import localnet_fixture +from construct import ListContainer +from pytest import fixture, mark +from pytest_asyncio import fixture as async_fixture +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Confirmed, Processed +from solana.rpc.core import RPCException +from solders.hash import Hash +from solders.keypair import Keypair +from solders.message import Message +from solders.pubkey import Pubkey +from solders.transaction import VersionedTransaction + +from tests.client_gen.example_program_gen.accounts import State, State2 +from tests.client_gen.example_program_gen.errors import from_tx_error +from tests.client_gen.example_program_gen.errors.custom import SomeError +from tests.client_gen.example_program_gen.instructions import ( + InitializeWithValuesAccounts, + InitializeWithValuesArgs, + cause_error, + increment_state_when_present, + initialize, + initialize_with_values, + initialize_with_values2, +) +from tests.client_gen.example_program_gen.program_id import PROGRAM_ID +from tests.client_gen.example_program_gen.types import BarStruct, FooStruct +from tests.client_gen.example_program_gen.types.foo_enum import ( + Named, + NoFields, + Struct, + Unnamed, +) + +EXAMPLE_PROGRAM_DIR = Path("tests/client_gen/example-program") + + +@fixture(scope="session") +def event_loop(): + """Create an instance of the default event loop for each test case.""" + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() + + +localnet = localnet_fixture(EXAMPLE_PROGRAM_DIR, scope="session") + + +@fixture(scope="session") +def payer(localnet) -> Keypair: + with (EXAMPLE_PROGRAM_DIR / ".anchor/test-ledger/faucet-keypair.json").open() as f: + faucet_keypair_json: List[int] = json.load(f) + return Keypair.from_bytes(faucet_keypair_json) + + +@async_fixture(scope="session") +async def provider(localnet, payer: Keypair) -> AsyncGenerator[Provider, None]: + wallet = Wallet(payer) + conn = AsyncClient(commitment=Processed) + prov = Provider(conn, wallet) + yield prov + await prov.close() + + +@async_fixture(scope="session") +async def blockhash(provider: Provider) -> Hash: + return (await provider.connection.get_latest_blockhash(Confirmed)).value.blockhash + + +@async_fixture(scope="module") +async def init_and_account_fetch(provider: Provider, blockhash: Hash) -> Keypair: + state = Keypair() + initialize_ix = initialize( + { + "state": state.pubkey(), + "payer": provider.wallet.public_key, + } + ) + msg = Message.new_with_blockhash( + [initialize_ix], provider.wallet.public_key, blockhash + ) + tx = VersionedTransaction(msg, [provider.wallet.payer, state]) + await provider.send(tx) + return state + + +@mark.asyncio +async def test_init_and_account_fetch( + init_and_account_fetch: Keypair, provider: Provider +) -> None: + state = init_and_account_fetch + vec_struct_field_enum_field_expected = Named( + value={ + "bool_field": True, + "u8_field": 15, + "nested": BarStruct(some_field=True, other_field=10), + } + ) + assert vec_struct_field_enum_field_expected.discriminator == 2 + assert vec_struct_field_enum_field_expected.kind == "Named" + vec_struct_field_expected = [ + FooStruct( + field1=123, + field2=999, + nested=BarStruct(some_field=True, other_field=10), + vec_nested=[BarStruct(some_field=True, other_field=10)], + option_nested=BarStruct(some_field=True, other_field=10), + enum_field=vec_struct_field_enum_field_expected, + ) + ] + option_struct_field_expected = FooStruct( + field1=123, + field2=999, + nested=BarStruct(some_field=True, other_field=10), + vec_nested=[BarStruct(some_field=True, other_field=10)], + option_nested=BarStruct(some_field=True, other_field=10), + enum_field=Named( + value={ + "bool_field": True, + "u8_field": 15, + "nested": BarStruct(some_field=True, other_field=10), + } + ), + ) + struct_field_expected = FooStruct( + field1=123, + field2=999, + nested=BarStruct(some_field=True, other_field=10), + vec_nested=[BarStruct(some_field=True, other_field=10)], + option_nested=BarStruct(some_field=True, other_field=10), + enum_field=Named( + value={ + "bool_field": True, + "u8_field": 15, + "nested": BarStruct(some_field=True, other_field=10), + } + ), + ) + array_field_expected = ListContainer([True, False, True]) + vec_field_expected = ListContainer([1, 2, 100, 1000, 18446744073709551615]) + enum_field1_expected = Unnamed( + value=(False, 10, BarStruct(some_field=True, other_field=10)) + ) + assert enum_field1_expected.kind == "Unnamed" + assert enum_field1_expected.discriminator == 0 + enum_field2_expected = Named( + value={ + "bool_field": True, + "u8_field": 20, + "nested": BarStruct(some_field=True, other_field=10), + } + ) + assert enum_field2_expected.kind == "Named" + assert enum_field2_expected.discriminator == 2 + enum_field3_expected = Struct(value=(BarStruct(some_field=True, other_field=10),)) + assert enum_field3_expected.discriminator == 3 + assert enum_field3_expected.kind == "Struct" + enum_field4_expected = NoFields() + assert enum_field4_expected.discriminator == 6 + assert enum_field4_expected.kind == "NoFields" + expected = State( + bool_field=True, + u8_field=234, + i8_field=-123, + u16_field=62345, + i16_field=-31234, + u32_field=1234567891, + i32_field=-1234567891, + f32_field=123456.5, + u64_field=9223372036854775817, + i64_field=-4611686018427387914, + f64_field=1234567891.345, + u128_field=170141183460469231731687303715884105737, + i128_field=-85070591730234615865843651857942052874, + bytes_field=b"\x01\x02\xff\xfe", + string_field="hello", + pubkey_field=Pubkey.from_string("EPZP2wrcRtMxrAPJCXVEQaYD9eH7fH7h12YqKDcd4aS7"), + vec_field=vec_field_expected, + vec_struct_field=vec_struct_field_expected, + option_field=None, + option_struct_field=option_struct_field_expected, + struct_field=struct_field_expected, + array_field=array_field_expected, + enum_field1=enum_field1_expected, + enum_field2=enum_field2_expected, + enum_field3=enum_field3_expected, + enum_field4=enum_field4_expected, + ) + res = await State.fetch(provider.connection, state.pubkey()) + assert res == expected + res = await State.fetch(provider.connection, state.pubkey(), program_id=PROGRAM_ID) + assert res == expected + + +@async_fixture(scope="session") +async def setup_fetch_multiple( + provider: Provider, blockhash: Hash +) -> tuple[Keypair, Keypair]: + state = Keypair() + another_state = Keypair() + initialize_ixs = [ + initialize( + { + "state": state.pubkey(), + "payer": provider.wallet.public_key, + } + ), + initialize( + { + "state": another_state.pubkey(), + "payer": provider.wallet.public_key, + } + ), + ] + msg = Message.new_with_blockhash( + initialize_ixs, provider.wallet.public_key, blockhash + ) + tx = VersionedTransaction(msg, [provider.wallet.payer, state, another_state]) + await provider.send(tx) + return state, another_state + + +@mark.asyncio +async def test_fetch_multiple( + provider: Provider, setup_fetch_multiple: tuple[Keypair, Keypair] +) -> None: + state, another_state = setup_fetch_multiple + non_state = Keypair() + res = await State.fetch_multiple( + provider.connection, + [state.pubkey(), non_state.pubkey(), another_state.pubkey()], + ) + assert isinstance(res[0], State) + assert res[1] is None + assert isinstance(res[2], State) + + +@async_fixture(scope="session") +async def send_instructions_with_args( + provider: Provider, blockhash +) -> tuple[Keypair, Keypair]: + state = Keypair() + state2 = Keypair() + vec_struct_field = [ + FooStruct( + field1=1, + field2=2, + nested=BarStruct(some_field=True, other_field=55), + vec_nested=[BarStruct(some_field=False, other_field=11)], + option_nested=None, + enum_field=Unnamed((True, 22, BarStruct(some_field=True, other_field=33))), + ) + ] + struct_field = FooStruct( + field1=1, + field2=2, + nested=BarStruct(some_field=True, other_field=55), + vec_nested=[BarStruct(some_field=False, other_field=11)], + option_nested=None, + enum_field=NoFields(), + ) + enum_field1 = Unnamed((True, 15, BarStruct(some_field=False, other_field=200))) + enum_field2 = Named( + { + "bool_field": True, + "u8_field": 128, + "nested": BarStruct(some_field=False, other_field=1), + } + ) + enum_field3 = Struct((BarStruct(some_field=True, other_field=15),)) + initialize_with_values_args = InitializeWithValuesArgs( + bool_field=True, + u8_field=253, + i8_field=-120, + u16_field=61234, + i16_field=-31253, + u32_field=1234567899, + i32_field=-123456789, + f32_field=123458.5, + u64_field=9223372036854775810, + i64_field=-4611686018427387912, + f64_field=1234567892.445, + u128_field=170141183460469231731687303715884105740, + i128_field=-85070591730234615865843651857942052877, + bytes_field=bytes([5, 10, 255]), + string_field="string value", + pubkey_field=Pubkey.from_string("GDddEKTjLBqhskzSMYph5o54VYLQfPCR3PoFqKHLJK6s"), + vec_field=[1, 123456789123456789], + vec_struct_field=vec_struct_field, + option_field=True, + option_struct_field=None, + struct_field=struct_field, + array_field=[True, True, False], + enum_field1=enum_field1, + enum_field2=enum_field2, + enum_field3=enum_field3, + enum_field4=NoFields(), + ) + initialize_with_values_accounts = InitializeWithValuesAccounts( + state=state.pubkey(), + payer=provider.wallet.public_key, + ) + ix1 = initialize_with_values( + initialize_with_values_args, initialize_with_values_accounts + ) + ix2 = initialize_with_values2( + {"vec_of_option": [None, None, 20]}, + { + "state": state2.pubkey(), + "payer": provider.wallet.public_key, + }, + ) + msg = Message.new_with_blockhash([ix1, ix2], provider.wallet.public_key, blockhash) + tx = VersionedTransaction(msg, [provider.wallet.payer, state, state2]) + await provider.send(tx) + return state, state2 + + +@mark.asyncio +async def test_instructions_with_args( + send_instructions_with_args: tuple[Keypair, Keypair], provider: Provider +) -> None: + state, state2 = send_instructions_with_args + expected = State( + bool_field=True, + u8_field=253, + i8_field=-120, + u16_field=61234, + i16_field=-31253, + u32_field=1234567899, + i32_field=-123456789, + f32_field=123458.5, + u64_field=9223372036854775810, + i64_field=-4611686018427387912, + f64_field=1234567892.445, + u128_field=170141183460469231731687303715884105740, + i128_field=-85070591730234615865843651857942052877, + bytes_field=b"\x05\n\xff", + string_field="string value", + pubkey_field=Pubkey.from_string("GDddEKTjLBqhskzSMYph5o54VYLQfPCR3PoFqKHLJK6s"), + vec_field=ListContainer([1, 123456789123456789]), + vec_struct_field=[ + FooStruct( + field1=1, + field2=2, + nested=BarStruct(some_field=True, other_field=55), + vec_nested=[BarStruct(some_field=False, other_field=11)], + option_nested=None, + enum_field=Unnamed( + value=(True, 22, BarStruct(some_field=True, other_field=33)) + ), + ) + ], + option_field=True, + option_struct_field=None, + struct_field=FooStruct( + field1=1, + field2=2, + nested=BarStruct(some_field=True, other_field=55), + vec_nested=[BarStruct(some_field=False, other_field=11)], + option_nested=None, + enum_field=NoFields(), + ), + array_field=ListContainer([True, True, False]), + enum_field1=Unnamed( + value=(True, 15, BarStruct(some_field=False, other_field=200)) + ), + enum_field2=Named( + value={ + "bool_field": True, + "u8_field": 128, + "nested": BarStruct(some_field=False, other_field=1), + } + ), + enum_field3=Struct(value=(BarStruct(some_field=True, other_field=15),)), + enum_field4=NoFields(), + ) + expected2 = State2(vec_of_option=ListContainer([None, None, 20])) + res = await State.fetch(provider.connection, state.pubkey()) + res2 = await State2.fetch(provider.connection, state2.pubkey()) + assert res == expected + assert res2 == expected2 + + +@mark.asyncio +async def test_instruction_with_optional_account( + send_instructions_with_args: tuple[Keypair, Keypair], + provider: Provider, + blockhash: Hash, +) -> None: + def prepare_tx(ixs): + msg = Message.new_with_blockhash([ix], provider.wallet.public_key, blockhash) + tx = VersionedTransaction(msg, [provider.wallet.payer]) + return tx + + state, state2 = send_instructions_with_args + ix = increment_state_when_present( + { + "first_state": None, + "second_state": state2.pubkey(), + }, + ) + before_res = await State.fetch(provider.connection, state.pubkey()) + assert before_res is not None + tx = prepare_tx(ix) + await provider.send(tx) + + res = await State.fetch(provider.connection, state.pubkey()) + assert res is not None + assert before_res.u8_field == res.u8_field + + ix = increment_state_when_present( + { + "first_state": state.pubkey(), + "second_state": state2.pubkey(), + }, + ) + + res = await State.fetch(provider.connection, state.pubkey()) + tx = prepare_tx(ix) + await provider.send(tx) + + res = await State.fetch(provider.connection, state.pubkey()) + assert res is not None + assert before_res.u8_field + 1 == res.u8_field + + +@mark.asyncio +async def test_cause_error(provider: Provider, blockhash: Hash) -> None: + msg = Message.new_with_blockhash( + [cause_error()], provider.wallet.public_key, blockhash + ) + tx = VersionedTransaction(msg, [provider.wallet.payer]) + try: + await provider.send(tx) + except RPCException as exc: + caught = from_tx_error(exc) + assert isinstance(caught, SomeError) diff --git a/basics/anchorpy-main/tests/client-gen/test_functional.py b/basics/anchorpy-main/tests/client-gen/test_functional.py new file mode 100644 index 0000000..aa3d2db --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/test_functional.py @@ -0,0 +1,357 @@ +import json +from filecmp import dircmp +from pathlib import Path + +from anchorpy.cli import client_gen +from py.path import local +from solana.rpc.core import RPCException +from solders.pubkey import Pubkey +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from solders.rpc.responses import SimulateTransactionResp + +from tests.client_gen.example_program_gen.accounts import State +from tests.client_gen.example_program_gen.errors import from_tx_error +from tests.client_gen.example_program_gen.errors.anchor import InvalidProgramId +from tests.client_gen.example_program_gen.types import BarStruct, FooStruct +from tests.client_gen.example_program_gen.types.foo_enum import ( + Named, + NamedValue, + NoFields, + Struct, + Unnamed, +) + + +def test_quarry_mine(tmpdir: local) -> None: + proj_dir = Path(tmpdir) + out_dir = proj_dir / "generated" + idl_path = Path("tests/idls/quarry_mine.json") + client_gen(idl_path, out_dir, "placeholder") + + +def test_merkle_distributor(tmpdir: local) -> None: + proj_dir = Path(tmpdir) + out_dir = proj_dir / "generated" + idl_path = Path("tests/idls/merkle_distributor.json") + client_gen(idl_path, out_dir, "placeholder") + + +def test_null_err_when_cpi_fails() -> None: + to_dump = { + "jsonrpc": "2.0", + "error": { + "code": -32002, + "message": "", + "data": { + "err": {"InstructionError": [0, {"Custom": 3}]}, + "logs": [ + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 invoke [1]", + "Program log: Instruction: CauseError", + "Program 11111111111111111111111111111111 invoke [2]", + "Allocate: requested 1000000000000000000, max allowed 10485760", + "Program 11111111111111111111111111111111 failed: custom program error: 0x3", + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 consumed 7958 of 1400000 compute units", + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 failed: custom program error: 0x3", + ], + }, + }, + } + raw = json.dumps(to_dump) + parsed = SimulateTransactionResp.from_json(raw) + assert isinstance(parsed, SendTransactionPreflightFailureMessage) + err_mock = RPCException(parsed) + assert from_tx_error(err_mock) is None + + +def test_parses_anchor_error() -> None: + to_dump = { + "jsonrpc": "2.0", + "error": { + "code": -32002, + "message": "", + "data": { + "err": {"InstructionError": [0, {"Custom": 3008}]}, + "logs": [ + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 invoke [1]", + "Program log: Instruction: CauseError", + "Program log: AnchorError caused by account: system_program. Error Code: InvalidProgramId. Error Number: 3008. Error Message: Program ID was not as expected.", + "Program log: Left:", + "Program log: 24S58Cp5Myf6iGx4umBNd7RgDrZ9nkKzvkfFHBMDomNa", + "Program log: Right:", + "Program log: 11111111111111111111111111111111", + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 consumed 5043 of 1400000 compute units", + "Program 3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8 failed: custom program error: 0xbc0", + ], + }, + }, + } + raw = json.dumps(to_dump) + parsed = SimulateTransactionResp.from_json(raw) + assert isinstance(parsed, SendTransactionPreflightFailureMessage) + err_mock = RPCException(parsed) + assert isinstance(from_tx_error(err_mock), InvalidProgramId) + + +def test_json() -> None: + vec_struct_field = [ + FooStruct( + field1=5, + field2=6, + nested=BarStruct( + some_field=True, + other_field=15, + ), + vec_nested=[ + BarStruct( + some_field=True, + other_field=13, + ), + ], + option_nested=None, + enum_field=Unnamed( + ( + False, + 111, + BarStruct( + some_field=False, + other_field=11, + ), + ) + ), + ), + ] + option_struct_field = FooStruct( + field1=8, + field2=9, + nested=BarStruct( + some_field=True, + other_field=17, + ), + vec_nested=[ + BarStruct( + some_field=True, + other_field=10, + ), + ], + option_nested=BarStruct( + some_field=False, + other_field=99, + ), + enum_field=NoFields(), + ) + struct_field = FooStruct( + field1=11, + field2=12, + nested=BarStruct( + some_field=False, + other_field=177, + ), + vec_nested=[ + BarStruct( + some_field=True, + other_field=15, + ), + ], + option_nested=BarStruct( + some_field=True, + other_field=75, + ), + enum_field=NoFields(), + ) + enum_field1 = Unnamed( + ( + False, + 157, + BarStruct( + some_field=True, + other_field=193, + ), + ) + ) + enum_field2 = Named( + NamedValue( + bool_field=False, + u8_field=77, + nested=BarStruct( + some_field=True, + other_field=100, + ), + ) + ) + enum_field3 = Struct( + ( + BarStruct( + some_field=False, + other_field=122, + ), + ) + ) + state = State( + bool_field=True, + u8_field=255, + i8_field=-120, + u16_field=62000, + i16_field=-31000, + u32_field=123456789, + i32_field=-123456789, + f32_field=123456.5, + u64_field=9223372036854775805, + i64_field=4611686018427387910, + f64_field=1234567891.35, + u128_field=170141183460469231731687303715884105760, + i128_field=-85070591730234615865843651857942052897, + bytes_field=bytes([1, 255]), + string_field="a string", + pubkey_field=Pubkey.from_string("EPZP2wrcRtMxrAPJCXVEQaYD9eH7fH7h12YqKDcd4aS7"), + vec_field=[10, 1234567890123456], + vec_struct_field=vec_struct_field, + option_field=None, + option_struct_field=option_struct_field, + struct_field=struct_field, + array_field=[True, False], + enum_field1=enum_field1, + enum_field2=enum_field2, + enum_field3=enum_field3, + enum_field4=NoFields(), + ) + state_json = state.to_json() + expected = { + "bool_field": True, + "u8_field": 255, + "i8_field": -120, + "u16_field": 62000, + "i16_field": -31000, + "u32_field": 123456789, + "i32_field": -123456789, + "f32_field": 123456.5, + "u64_field": 9223372036854775805, + "i64_field": 4611686018427387910, + "f64_field": 1234567891.35, + "u128_field": 170141183460469231731687303715884105760, + "i128_field": -85070591730234615865843651857942052897, + "bytes_field": [1, 255], + "string_field": "a string", + "pubkey_field": "EPZP2wrcRtMxrAPJCXVEQaYD9eH7fH7h12YqKDcd4aS7", + "vec_field": [10, 1234567890123456], + "vec_struct_field": [ + { + "field1": 5, + "field2": 6, + "nested": { + "some_field": True, + "other_field": 15, + }, + "vec_nested": [ + { + "some_field": True, + "other_field": 13, + }, + ], + "option_nested": None, + "enum_field": { + "kind": "Unnamed", + "value": ( + False, + 111, + { + "some_field": False, + "other_field": 11, + }, + ), + }, + }, + ], + "option_field": None, + "option_struct_field": { + "field1": 8, + "field2": 9, + "nested": { + "some_field": True, + "other_field": 17, + }, + "vec_nested": [ + { + "some_field": True, + "other_field": 10, + }, + ], + "option_nested": { + "some_field": False, + "other_field": 99, + }, + "enum_field": { + "kind": "NoFields", + }, + }, + "struct_field": { + "field1": 11, + "field2": 12, + "nested": { + "some_field": False, + "other_field": 177, + }, + "vec_nested": [ + { + "some_field": True, + "other_field": 15, + }, + ], + "option_nested": { + "some_field": True, + "other_field": 75, + }, + "enum_field": { + "kind": "NoFields", + }, + }, + "array_field": [True, False], + "enum_field1": { + "kind": "Unnamed", + "value": ( + False, + 157, + { + "some_field": True, + "other_field": 193, + }, + ), + }, + "enum_field2": { + "kind": "Named", + "value": { + "bool_field": False, + "u8_field": 77, + "nested": { + "some_field": True, + "other_field": 100, + }, + }, + }, + "enum_field3": { + "kind": "Struct", + "value": ( + { + "some_field": False, + "other_field": 122, + }, + ), + }, + "enum_field4": { + "kind": "NoFields", + }, + } + assert state_json == expected + state_from_json = State.from_json(state_json) + assert state_from_json == state + + +def has_differences(dcmp: dircmp) -> bool: + differences = dcmp.left_only + dcmp.right_only + dcmp.diff_files + if differences: + return True + return any([has_differences(subdcmp) for subdcmp in dcmp.subdirs.values()]) + + +def test_generated_as_expected(project_dir: Path) -> None: + dcmp = dircmp(project_dir, "tests/client_gen/example_program_gen") + assert not has_differences(dcmp) diff --git a/basics/anchorpy-main/tests/client-gen/token/__init__.py b/basics/anchorpy-main/tests/client-gen/token/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/tests/client-gen/token/accounts/__init__.py b/basics/anchorpy-main/tests/client-gen/token/accounts/__init__.py new file mode 100644 index 0000000..95f3234 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/accounts/__init__.py @@ -0,0 +1,3 @@ +from .mint import Mint, MintJSON +from .account import Account, AccountJSON +from .multisig import Multisig, MultisigJSON diff --git a/basics/anchorpy-main/tests/client-gen/token/accounts/account.py b/basics/anchorpy-main/tests/client-gen/token/accounts/account.py new file mode 100644 index 0000000..d85015e --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/accounts/account.py @@ -0,0 +1,133 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey, COption +from ..program_id import PROGRAM_ID +from .. import types + + +class AccountJSON(typing.TypedDict): + mint: str + owner: str + amount: int + delegate: typing.Optional[str] + state: types.account_state.AccountStateJSON + is_native: typing.Optional[int] + delegated_amount: int + close_authority: typing.Optional[str] + + +@dataclass +class Account: + discriminator: typing.ClassVar = b"qB\xe06\xbcw\xf0e" + layout: typing.ClassVar = borsh.CStruct( + "mint" / BorshPubkey, + "owner" / BorshPubkey, + "amount" / borsh.U64, + "delegate" / COption(BorshPubkey), + "state" / types.account_state.layout, + "is_native" / COption(borsh.U64), + "delegated_amount" / borsh.U64, + "close_authority" / COption(BorshPubkey), + ) + mint: Pubkey + owner: Pubkey + amount: int + delegate: typing.Optional[Pubkey] + state: types.account_state.AccountStateKind + is_native: typing.Optional[int] + delegated_amount: int + close_authority: typing.Optional[Pubkey] + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["Account"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["Account"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["Account"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "Account": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = Account.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + mint=dec.mint, + owner=dec.owner, + amount=dec.amount, + delegate=dec.delegate, + state=types.account_state.from_decoded(dec.state), + is_native=dec.is_native, + delegated_amount=dec.delegated_amount, + close_authority=dec.close_authority, + ) + + def to_json(self) -> AccountJSON: + return { + "mint": str(self.mint), + "owner": str(self.owner), + "amount": self.amount, + "delegate": (None if self.delegate is None else str(self.delegate)), + "state": self.state.to_json(), + "is_native": self.is_native, + "delegated_amount": self.delegated_amount, + "close_authority": ( + None if self.close_authority is None else str(self.close_authority) + ), + } + + @classmethod + def from_json(cls, obj: AccountJSON) -> "Account": + return cls( + mint=Pubkey.from_string(obj["mint"]), + owner=Pubkey.from_string(obj["owner"]), + amount=obj["amount"], + delegate=( + None if obj["delegate"] is None else Pubkey.from_string(obj["delegate"]) + ), + state=types.account_state.from_json(obj["state"]), + is_native=obj["is_native"], + delegated_amount=obj["delegated_amount"], + close_authority=( + None + if obj["close_authority"] is None + else Pubkey.from_string(obj["close_authority"]) + ), + ) diff --git a/basics/anchorpy-main/tests/client-gen/token/accounts/mint.py b/basics/anchorpy-main/tests/client-gen/token/accounts/mint.py new file mode 100644 index 0000000..8df31bb --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/accounts/mint.py @@ -0,0 +1,118 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey, COption +from ..program_id import PROGRAM_ID + + +class MintJSON(typing.TypedDict): + mint_authority: typing.Optional[str] + supply: int + decimals: int + is_initialized: bool + freeze_authority: typing.Optional[str] + + +@dataclass +class Mint: + discriminator: typing.ClassVar = b"P\xbc\xf5\x14_\x8a9\x9c" + layout: typing.ClassVar = borsh.CStruct( + "mint_authority" / COption(BorshPubkey), + "supply" / borsh.U64, + "decimals" / borsh.U8, + "is_initialized" / borsh.Bool, + "freeze_authority" / COption(BorshPubkey), + ) + mint_authority: typing.Optional[Pubkey] + supply: int + decimals: int + is_initialized: bool + freeze_authority: typing.Optional[Pubkey] + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["Mint"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["Mint"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["Mint"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "Mint": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = Mint.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + mint_authority=dec.mint_authority, + supply=dec.supply, + decimals=dec.decimals, + is_initialized=dec.is_initialized, + freeze_authority=dec.freeze_authority, + ) + + def to_json(self) -> MintJSON: + return { + "mint_authority": ( + None if self.mint_authority is None else str(self.mint_authority) + ), + "supply": self.supply, + "decimals": self.decimals, + "is_initialized": self.is_initialized, + "freeze_authority": ( + None if self.freeze_authority is None else str(self.freeze_authority) + ), + } + + @classmethod + def from_json(cls, obj: MintJSON) -> "Mint": + return cls( + mint_authority=( + None + if obj["mint_authority"] is None + else Pubkey.from_string(obj["mint_authority"]) + ), + supply=obj["supply"], + decimals=obj["decimals"], + is_initialized=obj["is_initialized"], + freeze_authority=( + None + if obj["freeze_authority"] is None + else Pubkey.from_string(obj["freeze_authority"]) + ), + ) diff --git a/basics/anchorpy-main/tests/client-gen/token/accounts/multisig.py b/basics/anchorpy-main/tests/client-gen/token/accounts/multisig.py new file mode 100644 index 0000000..3b63a5c --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/accounts/multisig.py @@ -0,0 +1,100 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey +from ..program_id import PROGRAM_ID + + +class MultisigJSON(typing.TypedDict): + m: int + n: int + is_initialized: bool + signers: list[str] + + +@dataclass +class Multisig: + discriminator: typing.ClassVar = b"\xe0ty\xbaD\xa1O\xec" + layout: typing.ClassVar = borsh.CStruct( + "m" / borsh.U8, + "n" / borsh.U8, + "is_initialized" / borsh.Bool, + "signers" / BorshPubkey[11], + ) + m: int + n: int + is_initialized: bool + signers: list[Pubkey] + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["Multisig"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["Multisig"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["Multisig"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "Multisig": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = Multisig.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + m=dec.m, + n=dec.n, + is_initialized=dec.is_initialized, + signers=dec.signers, + ) + + def to_json(self) -> MultisigJSON: + return { + "m": self.m, + "n": self.n, + "is_initialized": self.is_initialized, + "signers": list(map(lambda item: str(item), self.signers)), + } + + @classmethod + def from_json(cls, obj: MultisigJSON) -> "Multisig": + return cls( + m=obj["m"], + n=obj["n"], + is_initialized=obj["is_initialized"], + signers=list(map(lambda item: Pubkey.from_string(item), obj["signers"])), + ) diff --git a/basics/anchorpy-main/tests/client-gen/token/errors/__init__.py b/basics/anchorpy-main/tests/client-gen/token/errors/__init__.py new file mode 100644 index 0000000..421993d --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/errors/__init__.py @@ -0,0 +1,29 @@ +import typing +import re +from solders.transaction_status import ( + InstructionErrorCustom, + TransactionErrorInstructionError, +) +from solana.rpc.core import RPCException +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from anchorpy.error import extract_code_and_logs +from ..program_id import PROGRAM_ID +from . import anchor +from . import custom + + +def from_code(code: int) -> typing.Union[custom.CustomError, anchor.AnchorError, None]: + return custom.from_code(code) if code >= 6000 else anchor.from_code(code) + + +error_re = re.compile(r"Program (\w+) failed: custom program error: (\w+)") + + +def from_tx_error( + error: RPCException, +) -> typing.Union[anchor.AnchorError, custom.CustomError, None]: + err_info = error.args[0] + extracted = extract_code_and_logs(err_info, PROGRAM_ID) + if extracted is None: + return None + return from_code(extracted[0]) diff --git a/basics/anchorpy-main/tests/client-gen/token/errors/anchor.py b/basics/anchorpy-main/tests/client-gen/token/errors/anchor.py new file mode 100644 index 0000000..3f266ef --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/errors/anchor.py @@ -0,0 +1,590 @@ +import typing +from anchorpy.error import ProgramError + + +class InstructionMissing(ProgramError): + def __init__(self): + super().__init__(100, "8 byte instruction identifier not provided") + + code = 100 + name = "InstructionMissing" + msg = "8 byte instruction identifier not provided" + + +class InstructionFallbackNotFound(ProgramError): + def __init__(self): + super().__init__(101, "Fallback functions are not supported") + + code = 101 + name = "InstructionFallbackNotFound" + msg = "Fallback functions are not supported" + + +class InstructionDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(102, "The program could not deserialize the given instruction") + + code = 102 + name = "InstructionDidNotDeserialize" + msg = "The program could not deserialize the given instruction" + + +class InstructionDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(103, "The program could not serialize the given instruction") + + code = 103 + name = "InstructionDidNotSerialize" + msg = "The program could not serialize the given instruction" + + +class IdlInstructionStub(ProgramError): + def __init__(self): + super().__init__(1000, "The program was compiled without idl instructions") + + code = 1000 + name = "IdlInstructionStub" + msg = "The program was compiled without idl instructions" + + +class IdlInstructionInvalidProgram(ProgramError): + def __init__(self): + super().__init__( + 1001, "The transaction was given an invalid program for the IDL instruction" + ) + + code = 1001 + name = "IdlInstructionInvalidProgram" + msg = "The transaction was given an invalid program for the IDL instruction" + + +class ConstraintMut(ProgramError): + def __init__(self): + super().__init__(2000, "A mut constraint was violated") + + code = 2000 + name = "ConstraintMut" + msg = "A mut constraint was violated" + + +class ConstraintHasOne(ProgramError): + def __init__(self): + super().__init__(2001, "A has_one constraint was violated") + + code = 2001 + name = "ConstraintHasOne" + msg = "A has_one constraint was violated" + + +class ConstraintSigner(ProgramError): + def __init__(self): + super().__init__(2002, "A signer constraint was violated") + + code = 2002 + name = "ConstraintSigner" + msg = "A signer constraint was violated" + + +class ConstraintRaw(ProgramError): + def __init__(self): + super().__init__(2003, "A raw constraint was violated") + + code = 2003 + name = "ConstraintRaw" + msg = "A raw constraint was violated" + + +class ConstraintOwner(ProgramError): + def __init__(self): + super().__init__(2004, "An owner constraint was violated") + + code = 2004 + name = "ConstraintOwner" + msg = "An owner constraint was violated" + + +class ConstraintRentExempt(ProgramError): + def __init__(self): + super().__init__(2005, "A rent exempt constraint was violated") + + code = 2005 + name = "ConstraintRentExempt" + msg = "A rent exempt constraint was violated" + + +class ConstraintSeeds(ProgramError): + def __init__(self): + super().__init__(2006, "A seeds constraint was violated") + + code = 2006 + name = "ConstraintSeeds" + msg = "A seeds constraint was violated" + + +class ConstraintExecutable(ProgramError): + def __init__(self): + super().__init__(2007, "An executable constraint was violated") + + code = 2007 + name = "ConstraintExecutable" + msg = "An executable constraint was violated" + + +class ConstraintState(ProgramError): + def __init__(self): + super().__init__(2008, "A state constraint was violated") + + code = 2008 + name = "ConstraintState" + msg = "A state constraint was violated" + + +class ConstraintAssociated(ProgramError): + def __init__(self): + super().__init__(2009, "An associated constraint was violated") + + code = 2009 + name = "ConstraintAssociated" + msg = "An associated constraint was violated" + + +class ConstraintAssociatedInit(ProgramError): + def __init__(self): + super().__init__(2010, "An associated init constraint was violated") + + code = 2010 + name = "ConstraintAssociatedInit" + msg = "An associated init constraint was violated" + + +class ConstraintClose(ProgramError): + def __init__(self): + super().__init__(2011, "A close constraint was violated") + + code = 2011 + name = "ConstraintClose" + msg = "A close constraint was violated" + + +class ConstraintAddress(ProgramError): + def __init__(self): + super().__init__(2012, "An address constraint was violated") + + code = 2012 + name = "ConstraintAddress" + msg = "An address constraint was violated" + + +class ConstraintZero(ProgramError): + def __init__(self): + super().__init__(2013, "Expected zero account discriminant") + + code = 2013 + name = "ConstraintZero" + msg = "Expected zero account discriminant" + + +class ConstraintTokenMint(ProgramError): + def __init__(self): + super().__init__(2014, "A token mint constraint was violated") + + code = 2014 + name = "ConstraintTokenMint" + msg = "A token mint constraint was violated" + + +class ConstraintTokenOwner(ProgramError): + def __init__(self): + super().__init__(2015, "A token owner constraint was violated") + + code = 2015 + name = "ConstraintTokenOwner" + msg = "A token owner constraint was violated" + + +class ConstraintMintMintAuthority(ProgramError): + def __init__(self): + super().__init__(2016, "A mint mint authority constraint was violated") + + code = 2016 + name = "ConstraintMintMintAuthority" + msg = "A mint mint authority constraint was violated" + + +class ConstraintMintFreezeAuthority(ProgramError): + def __init__(self): + super().__init__(2017, "A mint freeze authority constraint was violated") + + code = 2017 + name = "ConstraintMintFreezeAuthority" + msg = "A mint freeze authority constraint was violated" + + +class ConstraintMintDecimals(ProgramError): + def __init__(self): + super().__init__(2018, "A mint decimals constraint was violated") + + code = 2018 + name = "ConstraintMintDecimals" + msg = "A mint decimals constraint was violated" + + +class ConstraintSpace(ProgramError): + def __init__(self): + super().__init__(2019, "A space constraint was violated") + + code = 2019 + name = "ConstraintSpace" + msg = "A space constraint was violated" + + +class RequireViolated(ProgramError): + def __init__(self): + super().__init__(2500, "A require expression was violated") + + code = 2500 + name = "RequireViolated" + msg = "A require expression was violated" + + +class RequireEqViolated(ProgramError): + def __init__(self): + super().__init__(2501, "A require_eq expression was violated") + + code = 2501 + name = "RequireEqViolated" + msg = "A require_eq expression was violated" + + +class RequireKeysEqViolated(ProgramError): + def __init__(self): + super().__init__(2502, "A require_keys_eq expression was violated") + + code = 2502 + name = "RequireKeysEqViolated" + msg = "A require_keys_eq expression was violated" + + +class RequireNeqViolated(ProgramError): + def __init__(self): + super().__init__(2503, "A require_neq expression was violated") + + code = 2503 + name = "RequireNeqViolated" + msg = "A require_neq expression was violated" + + +class RequireKeysNeqViolated(ProgramError): + def __init__(self): + super().__init__(2504, "A require_keys_neq expression was violated") + + code = 2504 + name = "RequireKeysNeqViolated" + msg = "A require_keys_neq expression was violated" + + +class RequireGtViolated(ProgramError): + def __init__(self): + super().__init__(2505, "A require_gt expression was violated") + + code = 2505 + name = "RequireGtViolated" + msg = "A require_gt expression was violated" + + +class RequireGteViolated(ProgramError): + def __init__(self): + super().__init__(2506, "A require_gte expression was violated") + + code = 2506 + name = "RequireGteViolated" + msg = "A require_gte expression was violated" + + +class AccountDiscriminatorAlreadySet(ProgramError): + def __init__(self): + super().__init__( + 3000, "The account discriminator was already set on this account" + ) + + code = 3000 + name = "AccountDiscriminatorAlreadySet" + msg = "The account discriminator was already set on this account" + + +class AccountDiscriminatorNotFound(ProgramError): + def __init__(self): + super().__init__(3001, "No 8 byte discriminator was found on the account") + + code = 3001 + name = "AccountDiscriminatorNotFound" + msg = "No 8 byte discriminator was found on the account" + + +class AccountDiscriminatorMismatch(ProgramError): + def __init__(self): + super().__init__(3002, "8 byte discriminator did not match what was expected") + + code = 3002 + name = "AccountDiscriminatorMismatch" + msg = "8 byte discriminator did not match what was expected" + + +class AccountDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(3003, "Failed to deserialize the account") + + code = 3003 + name = "AccountDidNotDeserialize" + msg = "Failed to deserialize the account" + + +class AccountDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(3004, "Failed to serialize the account") + + code = 3004 + name = "AccountDidNotSerialize" + msg = "Failed to serialize the account" + + +class AccountNotEnoughKeys(ProgramError): + def __init__(self): + super().__init__(3005, "Not enough account keys given to the instruction") + + code = 3005 + name = "AccountNotEnoughKeys" + msg = "Not enough account keys given to the instruction" + + +class AccountNotMutable(ProgramError): + def __init__(self): + super().__init__(3006, "The given account is not mutable") + + code = 3006 + name = "AccountNotMutable" + msg = "The given account is not mutable" + + +class AccountOwnedByWrongProgram(ProgramError): + def __init__(self): + super().__init__( + 3007, "The given account is owned by a different program than expected" + ) + + code = 3007 + name = "AccountOwnedByWrongProgram" + msg = "The given account is owned by a different program than expected" + + +class InvalidProgramId(ProgramError): + def __init__(self): + super().__init__(3008, "Program ID was not as expected") + + code = 3008 + name = "InvalidProgramId" + msg = "Program ID was not as expected" + + +class InvalidProgramExecutable(ProgramError): + def __init__(self): + super().__init__(3009, "Program account is not executable") + + code = 3009 + name = "InvalidProgramExecutable" + msg = "Program account is not executable" + + +class AccountNotSigner(ProgramError): + def __init__(self): + super().__init__(3010, "The given account did not sign") + + code = 3010 + name = "AccountNotSigner" + msg = "The given account did not sign" + + +class AccountNotSystemOwned(ProgramError): + def __init__(self): + super().__init__(3011, "The given account is not owned by the system program") + + code = 3011 + name = "AccountNotSystemOwned" + msg = "The given account is not owned by the system program" + + +class AccountNotInitialized(ProgramError): + def __init__(self): + super().__init__( + 3012, "The program expected this account to be already initialized" + ) + + code = 3012 + name = "AccountNotInitialized" + msg = "The program expected this account to be already initialized" + + +class AccountNotProgramData(ProgramError): + def __init__(self): + super().__init__(3013, "The given account is not a program data account") + + code = 3013 + name = "AccountNotProgramData" + msg = "The given account is not a program data account" + + +class AccountNotAssociatedTokenAccount(ProgramError): + def __init__(self): + super().__init__(3014, "The given account is not the associated token account") + + code = 3014 + name = "AccountNotAssociatedTokenAccount" + msg = "The given account is not the associated token account" + + +class AccountSysvarMismatch(ProgramError): + def __init__(self): + super().__init__( + 3015, "The given public key does not match the required sysvar" + ) + + code = 3015 + name = "AccountSysvarMismatch" + msg = "The given public key does not match the required sysvar" + + +class StateInvalidAddress(ProgramError): + def __init__(self): + super().__init__( + 4000, "The given state account does not have the correct address" + ) + + code = 4000 + name = "StateInvalidAddress" + msg = "The given state account does not have the correct address" + + +class Deprecated(ProgramError): + def __init__(self): + super().__init__( + 5000, "The API being used is deprecated and should no longer be used" + ) + + code = 5000 + name = "Deprecated" + msg = "The API being used is deprecated and should no longer be used" + + +AnchorError = typing.Union[ + InstructionMissing, + InstructionFallbackNotFound, + InstructionDidNotDeserialize, + InstructionDidNotSerialize, + IdlInstructionStub, + IdlInstructionInvalidProgram, + ConstraintMut, + ConstraintHasOne, + ConstraintSigner, + ConstraintRaw, + ConstraintOwner, + ConstraintRentExempt, + ConstraintSeeds, + ConstraintExecutable, + ConstraintState, + ConstraintAssociated, + ConstraintAssociatedInit, + ConstraintClose, + ConstraintAddress, + ConstraintZero, + ConstraintTokenMint, + ConstraintTokenOwner, + ConstraintMintMintAuthority, + ConstraintMintFreezeAuthority, + ConstraintMintDecimals, + ConstraintSpace, + RequireViolated, + RequireEqViolated, + RequireKeysEqViolated, + RequireNeqViolated, + RequireKeysNeqViolated, + RequireGtViolated, + RequireGteViolated, + AccountDiscriminatorAlreadySet, + AccountDiscriminatorNotFound, + AccountDiscriminatorMismatch, + AccountDidNotDeserialize, + AccountDidNotSerialize, + AccountNotEnoughKeys, + AccountNotMutable, + AccountOwnedByWrongProgram, + InvalidProgramId, + InvalidProgramExecutable, + AccountNotSigner, + AccountNotSystemOwned, + AccountNotInitialized, + AccountNotProgramData, + AccountNotAssociatedTokenAccount, + AccountSysvarMismatch, + StateInvalidAddress, + Deprecated, +] +ANCHOR_ERROR_MAP: dict[int, AnchorError] = { + 100: InstructionMissing(), + 101: InstructionFallbackNotFound(), + 102: InstructionDidNotDeserialize(), + 103: InstructionDidNotSerialize(), + 1000: IdlInstructionStub(), + 1001: IdlInstructionInvalidProgram(), + 2000: ConstraintMut(), + 2001: ConstraintHasOne(), + 2002: ConstraintSigner(), + 2003: ConstraintRaw(), + 2004: ConstraintOwner(), + 2005: ConstraintRentExempt(), + 2006: ConstraintSeeds(), + 2007: ConstraintExecutable(), + 2008: ConstraintState(), + 2009: ConstraintAssociated(), + 2010: ConstraintAssociatedInit(), + 2011: ConstraintClose(), + 2012: ConstraintAddress(), + 2013: ConstraintZero(), + 2014: ConstraintTokenMint(), + 2015: ConstraintTokenOwner(), + 2016: ConstraintMintMintAuthority(), + 2017: ConstraintMintFreezeAuthority(), + 2018: ConstraintMintDecimals(), + 2019: ConstraintSpace(), + 2500: RequireViolated(), + 2501: RequireEqViolated(), + 2502: RequireKeysEqViolated(), + 2503: RequireNeqViolated(), + 2504: RequireKeysNeqViolated(), + 2505: RequireGtViolated(), + 2506: RequireGteViolated(), + 3000: AccountDiscriminatorAlreadySet(), + 3001: AccountDiscriminatorNotFound(), + 3002: AccountDiscriminatorMismatch(), + 3003: AccountDidNotDeserialize(), + 3004: AccountDidNotSerialize(), + 3005: AccountNotEnoughKeys(), + 3006: AccountNotMutable(), + 3007: AccountOwnedByWrongProgram(), + 3008: InvalidProgramId(), + 3009: InvalidProgramExecutable(), + 3010: AccountNotSigner(), + 3011: AccountNotSystemOwned(), + 3012: AccountNotInitialized(), + 3013: AccountNotProgramData(), + 3014: AccountNotAssociatedTokenAccount(), + 3015: AccountSysvarMismatch(), + 4000: StateInvalidAddress(), + 5000: Deprecated(), +} + + +def from_code(code: int) -> typing.Optional[AnchorError]: + maybe_err = ANCHOR_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/tests/client-gen/token/errors/custom.py b/basics/anchorpy-main/tests/client-gen/token/errors/custom.py new file mode 100644 index 0000000..15cabe4 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/errors/custom.py @@ -0,0 +1,239 @@ +import typing +from anchorpy.error import ProgramError + + +class NotRentExempt(ProgramError): + def __init__(self) -> None: + super().__init__(0, "Lamport balance below rent-exempt threshold") + + code = 0 + name = "NotRentExempt" + msg = "Lamport balance below rent-exempt threshold" + + +class InsufficientFunds(ProgramError): + def __init__(self) -> None: + super().__init__(1, "Insufficient funds") + + code = 1 + name = "InsufficientFunds" + msg = "Insufficient funds" + + +class InvalidMint(ProgramError): + def __init__(self) -> None: + super().__init__(2, "Invalid Mint") + + code = 2 + name = "InvalidMint" + msg = "Invalid Mint" + + +class MintMismatch(ProgramError): + def __init__(self) -> None: + super().__init__(3, "Account not associated with this Mint") + + code = 3 + name = "MintMismatch" + msg = "Account not associated with this Mint" + + +class OwnerMismatch(ProgramError): + def __init__(self) -> None: + super().__init__(4, "Owner does not match") + + code = 4 + name = "OwnerMismatch" + msg = "Owner does not match" + + +class FixedSupply(ProgramError): + def __init__(self) -> None: + super().__init__(5, "Fixed supply") + + code = 5 + name = "FixedSupply" + msg = "Fixed supply" + + +class AlreadyInUse(ProgramError): + def __init__(self) -> None: + super().__init__(6, "Already in use") + + code = 6 + name = "AlreadyInUse" + msg = "Already in use" + + +class InvalidNumberOfProvidedSigners(ProgramError): + def __init__(self) -> None: + super().__init__(7, "Invalid number of provided signers") + + code = 7 + name = "InvalidNumberOfProvidedSigners" + msg = "Invalid number of provided signers" + + +class InvalidNumberOfRequiredSigners(ProgramError): + def __init__(self) -> None: + super().__init__(8, "Invalid number of required signers") + + code = 8 + name = "InvalidNumberOfRequiredSigners" + msg = "Invalid number of required signers" + + +class UninitializedState(ProgramError): + def __init__(self) -> None: + super().__init__(9, "State is unititialized") + + code = 9 + name = "UninitializedState" + msg = "State is unititialized" + + +class NativeNotSupported(ProgramError): + def __init__(self) -> None: + super().__init__(10, "Instruction does not support native tokens") + + code = 10 + name = "NativeNotSupported" + msg = "Instruction does not support native tokens" + + +class NonNativeHasBalance(ProgramError): + def __init__(self) -> None: + super().__init__( + 11, "Non-native account can only be closed if its balance is zero" + ) + + code = 11 + name = "NonNativeHasBalance" + msg = "Non-native account can only be closed if its balance is zero" + + +class InvalidInstruction(ProgramError): + def __init__(self) -> None: + super().__init__(12, "Invalid instruction") + + code = 12 + name = "InvalidInstruction" + msg = "Invalid instruction" + + +class InvalidState(ProgramError): + def __init__(self) -> None: + super().__init__(13, "State is invalid for requested operation") + + code = 13 + name = "InvalidState" + msg = "State is invalid for requested operation" + + +class Overflow(ProgramError): + def __init__(self) -> None: + super().__init__(14, "Operation overflowed") + + code = 14 + name = "Overflow" + msg = "Operation overflowed" + + +class AuthorityTypeNotSupported(ProgramError): + def __init__(self) -> None: + super().__init__(15, "Account does not support specified authority type") + + code = 15 + name = "AuthorityTypeNotSupported" + msg = "Account does not support specified authority type" + + +class MintCannotFreeze(ProgramError): + def __init__(self) -> None: + super().__init__(16, "This token mint cannot freeze accounts") + + code = 16 + name = "MintCannotFreeze" + msg = "This token mint cannot freeze accounts" + + +class AccountFrozen(ProgramError): + def __init__(self) -> None: + super().__init__(17, "Account is frozen") + + code = 17 + name = "AccountFrozen" + msg = "Account is frozen" + + +class MintDecimalsMismatch(ProgramError): + def __init__(self) -> None: + super().__init__( + 18, "The provided decimals value different from the Mint decimals" + ) + + code = 18 + name = "MintDecimalsMismatch" + msg = "The provided decimals value different from the Mint decimals" + + +class NonNativeNotSupported(ProgramError): + def __init__(self) -> None: + super().__init__(19, "Instruction does not support non-native tokens") + + code = 19 + name = "NonNativeNotSupported" + msg = "Instruction does not support non-native tokens" + + +CustomError = typing.Union[ + NotRentExempt, + InsufficientFunds, + InvalidMint, + MintMismatch, + OwnerMismatch, + FixedSupply, + AlreadyInUse, + InvalidNumberOfProvidedSigners, + InvalidNumberOfRequiredSigners, + UninitializedState, + NativeNotSupported, + NonNativeHasBalance, + InvalidInstruction, + InvalidState, + Overflow, + AuthorityTypeNotSupported, + MintCannotFreeze, + AccountFrozen, + MintDecimalsMismatch, + NonNativeNotSupported, +] +CUSTOM_ERROR_MAP: dict[int, CustomError] = { + 0: NotRentExempt(), + 1: InsufficientFunds(), + 2: InvalidMint(), + 3: MintMismatch(), + 4: OwnerMismatch(), + 5: FixedSupply(), + 6: AlreadyInUse(), + 7: InvalidNumberOfProvidedSigners(), + 8: InvalidNumberOfRequiredSigners(), + 9: UninitializedState(), + 10: NativeNotSupported(), + 11: NonNativeHasBalance(), + 12: InvalidInstruction(), + 13: InvalidState(), + 14: Overflow(), + 15: AuthorityTypeNotSupported(), + 16: MintCannotFreeze(), + 17: AccountFrozen(), + 18: MintDecimalsMismatch(), + 19: NonNativeNotSupported(), +} + + +def from_code(code: int) -> typing.Optional[CustomError]: + maybe_err = CUSTOM_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/__init__.py b/basics/anchorpy-main/tests/client-gen/token/instructions/__init__.py new file mode 100644 index 0000000..7167c3b --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/__init__.py @@ -0,0 +1,60 @@ +from .initialize_mint import initialize_mint, InitializeMintArgs, InitializeMintAccounts +from .initialize_account import initialize_account, InitializeAccountAccounts +from .initialize_multisig import ( + initialize_multisig, + InitializeMultisigArgs, + InitializeMultisigAccounts, +) +from .transfer import transfer, TransferArgs, TransferAccounts +from .approve import approve, ApproveArgs, ApproveAccounts +from .revoke import revoke, RevokeAccounts +from .set_authority import set_authority, SetAuthorityArgs, SetAuthorityAccounts +from .mint_to import mint_to, MintToArgs, MintToAccounts +from .burn import burn, BurnArgs, BurnAccounts +from .close_account import close_account, CloseAccountAccounts +from .freeze_account import freeze_account, FreezeAccountAccounts +from .thaw_account import thaw_account, ThawAccountAccounts +from .transfer_checked import ( + transfer_checked, + TransferCheckedArgs, + TransferCheckedAccounts, +) +from .approve_checked import approve_checked, ApproveCheckedArgs, ApproveCheckedAccounts +from .mint_to_checked import mint_to_checked, MintToCheckedArgs, MintToCheckedAccounts +from .burn_checked import burn_checked, BurnCheckedArgs, BurnCheckedAccounts +from .initialize_account2 import ( + initialize_account2, + InitializeAccount2Args, + InitializeAccount2Accounts, +) +from .sync_native import sync_native, SyncNativeAccounts +from .initialize_account3 import ( + initialize_account3, + InitializeAccount3Args, + InitializeAccount3Accounts, +) +from .initialize_multisig2 import ( + initialize_multisig2, + InitializeMultisig2Args, + InitializeMultisig2Accounts, +) +from .initialize_mint2 import ( + initialize_mint2, + InitializeMint2Args, + InitializeMint2Accounts, +) +from .get_account_data_size import get_account_data_size, GetAccountDataSizeAccounts +from .initialize_immutable_owner import ( + initialize_immutable_owner, + InitializeImmutableOwnerAccounts, +) +from .amount_to_ui_amount import ( + amount_to_ui_amount, + AmountToUiAmountArgs, + AmountToUiAmountAccounts, +) +from .ui_amount_to_amount import ( + ui_amount_to_amount, + UiAmountToAmountArgs, + UiAmountToAmountAccounts, +) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/amount_to_ui_amount.py b/basics/anchorpy-main/tests/client-gen/token/instructions/amount_to_ui_amount.py new file mode 100644 index 0000000..a19f397 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/amount_to_ui_amount.py @@ -0,0 +1,38 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class AmountToUiAmountArgs(typing.TypedDict): + amount: int + + +layout = borsh.CStruct("amount" / borsh.U64) + + +class AmountToUiAmountAccounts(typing.TypedDict): + mint: Pubkey + + +def amount_to_ui_amount( + args: AmountToUiAmountArgs, + accounts: AmountToUiAmountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xa0\x91\xc8b\xf2\x9c\x1eZ" + encoded_args = layout.build( + { + "amount": args["amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/approve.py b/basics/anchorpy-main/tests/client-gen/token/instructions/approve.py new file mode 100644 index 0000000..2916385 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/approve.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class ApproveArgs(typing.TypedDict): + amount: int + + +layout = borsh.CStruct("amount" / borsh.U64) + + +class ApproveAccounts(typing.TypedDict): + source: Pubkey + delegate: Pubkey + owner: Pubkey + + +def approve( + args: ApproveArgs, + accounts: ApproveAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["source"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["delegate"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"EJ\xd9$suaL" + encoded_args = layout.build( + { + "amount": args["amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/approve_checked.py b/basics/anchorpy-main/tests/client-gen/token/instructions/approve_checked.py new file mode 100644 index 0000000..b734d0a --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/approve_checked.py @@ -0,0 +1,46 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class ApproveCheckedArgs(typing.TypedDict): + amount: int + decimals: int + + +layout = borsh.CStruct("amount" / borsh.U64, "decimals" / borsh.U8) + + +class ApproveCheckedAccounts(typing.TypedDict): + source: Pubkey + mint: Pubkey + delegate: Pubkey + owner: Pubkey + + +def approve_checked( + args: ApproveCheckedArgs, + accounts: ApproveCheckedAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["source"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["delegate"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"/\xc5\xfe*:\xc9:m" + encoded_args = layout.build( + { + "amount": args["amount"], + "decimals": args["decimals"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/burn.py b/basics/anchorpy-main/tests/client-gen/token/instructions/burn.py new file mode 100644 index 0000000..be55863 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/burn.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class BurnArgs(typing.TypedDict): + amount: int + + +layout = borsh.CStruct("amount" / borsh.U64) + + +class BurnAccounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + authority: Pubkey + + +def burn( + args: BurnArgs, + accounts: BurnAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["authority"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"tn\x1d8k\xdb*]" + encoded_args = layout.build( + { + "amount": args["amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/burn_checked.py b/basics/anchorpy-main/tests/client-gen/token/instructions/burn_checked.py new file mode 100644 index 0000000..913224a --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/burn_checked.py @@ -0,0 +1,44 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class BurnCheckedArgs(typing.TypedDict): + amount: int + decimals: int + + +layout = borsh.CStruct("amount" / borsh.U64, "decimals" / borsh.U8) + + +class BurnCheckedAccounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + authority: Pubkey + + +def burn_checked( + args: BurnCheckedArgs, + accounts: BurnCheckedAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["authority"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xc6y\xc8fx\xd0\x9b\xb2" + encoded_args = layout.build( + { + "amount": args["amount"], + "decimals": args["decimals"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/close_account.py b/basics/anchorpy-main/tests/client-gen/token/instructions/close_account.py new file mode 100644 index 0000000..c6bf6de --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/close_account.py @@ -0,0 +1,29 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class CloseAccountAccounts(typing.TypedDict): + account: Pubkey + destination: Pubkey + owner: Pubkey + + +def close_account( + accounts: CloseAccountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["destination"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b'}\xff\x95\x0en"H\x18' + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/freeze_account.py b/basics/anchorpy-main/tests/client-gen/token/instructions/freeze_account.py new file mode 100644 index 0000000..921cded --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/freeze_account.py @@ -0,0 +1,29 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class FreezeAccountAccounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + owner: Pubkey + + +def freeze_account( + accounts: FreezeAccountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xfdKR\x85\xa7\xee+\x82" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/get_account_data_size.py b/basics/anchorpy-main/tests/client-gen/token/instructions/get_account_data_size.py new file mode 100644 index 0000000..f5a55f4 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/get_account_data_size.py @@ -0,0 +1,25 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class GetAccountDataSizeAccounts(typing.TypedDict): + mint: Pubkey + + +def get_account_data_size( + accounts: GetAccountDataSizeAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x10\xb1\xd2\x80\x15-o\x1f" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account.py new file mode 100644 index 0000000..288bef6 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account.py @@ -0,0 +1,31 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.sysvar import RENT +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class InitializeAccountAccounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + owner: Pubkey + + +def initialize_account( + accounts: InitializeAccountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["owner"], is_signer=False, is_writable=False), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"Jsc]\xc5Eg\x07" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account2.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account2.py new file mode 100644 index 0000000..b3257fb --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account2.py @@ -0,0 +1,43 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.sysvar import RENT +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeAccount2Args(typing.TypedDict): + owner: Pubkey + + +layout = borsh.CStruct("owner" / BorshPubkey) + + +class InitializeAccount2Accounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + + +def initialize_account2( + args: InitializeAccount2Args, + accounts: InitializeAccount2Accounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x08\xb6\x95\x90\xb9\x1f\xd1i" + encoded_args = layout.build( + { + "owner": args["owner"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account3.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account3.py new file mode 100644 index 0000000..83427fb --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_account3.py @@ -0,0 +1,41 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeAccount3Args(typing.TypedDict): + owner: Pubkey + + +layout = borsh.CStruct("owner" / BorshPubkey) + + +class InitializeAccount3Accounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + + +def initialize_account3( + args: InitializeAccount3Args, + accounts: InitializeAccount3Accounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x17\x8e\x8c\x87\x15\xa0\x85@" + encoded_args = layout.build( + { + "owner": args["owner"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_immutable_owner.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_immutable_owner.py new file mode 100644 index 0000000..ffea985 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_immutable_owner.py @@ -0,0 +1,25 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class InitializeImmutableOwnerAccounts(typing.TypedDict): + account: Pubkey + + +def initialize_immutable_owner( + accounts: InitializeImmutableOwnerAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b'\x8d2\x0f,\xc3\xf7"<' + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint.py new file mode 100644 index 0000000..c2b94af --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint.py @@ -0,0 +1,49 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.sysvar import RENT +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey, COption +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeMintArgs(typing.TypedDict): + decimals: int + mint_authority: Pubkey + freeze_authority: typing.Optional[Pubkey] + + +layout = borsh.CStruct( + "decimals" / borsh.U8, + "mint_authority" / BorshPubkey, + "freeze_authority" / COption(BorshPubkey), +) + + +class InitializeMintAccounts(typing.TypedDict): + mint: Pubkey + + +def initialize_mint( + args: InitializeMintArgs, + accounts: InitializeMintAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xd1*\xc3\x04\x81U\xd1," + encoded_args = layout.build( + { + "decimals": args["decimals"], + "mint_authority": args["mint_authority"], + "freeze_authority": args["freeze_authority"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint2.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint2.py new file mode 100644 index 0000000..ef12ee6 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_mint2.py @@ -0,0 +1,47 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey, COption +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeMint2Args(typing.TypedDict): + decimals: int + mint_authority: Pubkey + freeze_authority: typing.Optional[Pubkey] + + +layout = borsh.CStruct( + "decimals" / borsh.U8, + "mint_authority" / BorshPubkey, + "freeze_authority" / COption(BorshPubkey), +) + + +class InitializeMint2Accounts(typing.TypedDict): + mint: Pubkey + + +def initialize_mint2( + args: InitializeMint2Args, + accounts: InitializeMint2Accounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"_l\xc6\xd2H\xf3\x8f\xeb" + encoded_args = layout.build( + { + "decimals": args["decimals"], + "mint_authority": args["mint_authority"], + "freeze_authority": args["freeze_authority"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig.py new file mode 100644 index 0000000..e5a7374 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig.py @@ -0,0 +1,40 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.sysvar import RENT +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeMultisigArgs(typing.TypedDict): + m: int + + +layout = borsh.CStruct("m" / borsh.U8) + + +class InitializeMultisigAccounts(typing.TypedDict): + multisig: Pubkey + + +def initialize_multisig( + args: InitializeMultisigArgs, + accounts: InitializeMultisigAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["multisig"], is_signer=False, is_writable=True), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xdc\x82u\x15\x1b\xe3N\xd5" + encoded_args = layout.build( + { + "m": args["m"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig2.py b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig2.py new file mode 100644 index 0000000..2d77574 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/initialize_multisig2.py @@ -0,0 +1,40 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class InitializeMultisig2Args(typing.TypedDict): + m: int + + +layout = borsh.CStruct("m" / borsh.U8) + + +class InitializeMultisig2Accounts(typing.TypedDict): + multisig: Pubkey + signer: Pubkey + + +def initialize_multisig2( + args: InitializeMultisig2Args, + accounts: InitializeMultisig2Accounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["multisig"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["signer"], is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"Q\xefI'\x1b\x94\x02\x92" + encoded_args = layout.build( + { + "m": args["m"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to.py b/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to.py new file mode 100644 index 0000000..947566c --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class MintToArgs(typing.TypedDict): + amount: int + + +layout = borsh.CStruct("amount" / borsh.U64) + + +class MintToAccounts(typing.TypedDict): + mint: Pubkey + account: Pubkey + owner: Pubkey + + +def mint_to( + args: MintToArgs, + accounts: MintToAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b'\xf1"0\xba%\xb3{\xc0' + encoded_args = layout.build( + { + "amount": args["amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to_checked.py b/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to_checked.py new file mode 100644 index 0000000..f4ba48c --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/mint_to_checked.py @@ -0,0 +1,44 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class MintToCheckedArgs(typing.TypedDict): + amount: int + decimals: int + + +layout = borsh.CStruct("amount" / borsh.U64, "decimals" / borsh.U8) + + +class MintToCheckedAccounts(typing.TypedDict): + mint: Pubkey + account: Pubkey + owner: Pubkey + + +def mint_to_checked( + args: MintToCheckedArgs, + accounts: MintToCheckedAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xe5\xec$\xf0v\xe1-}" + encoded_args = layout.build( + { + "amount": args["amount"], + "decimals": args["decimals"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/revoke.py b/basics/anchorpy-main/tests/client-gen/token/instructions/revoke.py new file mode 100644 index 0000000..54009f8 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/revoke.py @@ -0,0 +1,27 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class RevokeAccounts(typing.TypedDict): + source: Pubkey + owner: Pubkey + + +def revoke( + accounts: RevokeAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["source"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b'\xaa\x17\x1f"\x85\xad]\xf2' + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/set_authority.py b/basics/anchorpy-main/tests/client-gen/token/instructions/set_authority.py new file mode 100644 index 0000000..51ad91c --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/set_authority.py @@ -0,0 +1,49 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey, COption +import borsh_construct as borsh +from .. import types +from ..program_id import PROGRAM_ID + + +class SetAuthorityArgs(typing.TypedDict): + authority_type: types.authority_type.AuthorityTypeKind + new_authority: typing.Optional[Pubkey] + + +layout = borsh.CStruct( + "authority_type" / types.authority_type.layout, + "new_authority" / COption(BorshPubkey), +) + + +class SetAuthorityAccounts(typing.TypedDict): + owned: Pubkey + owner: Pubkey + signer: Pubkey + + +def set_authority( + args: SetAuthorityArgs, + accounts: SetAuthorityAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["owned"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + AccountMeta(pubkey=accounts["signer"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x85\xfa%\x15n\xa3\x1ay" + encoded_args = layout.build( + { + "authority_type": args["authority_type"].to_encodable(), + "new_authority": args["new_authority"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/sync_native.py b/basics/anchorpy-main/tests/client-gen/token/instructions/sync_native.py new file mode 100644 index 0000000..d615229 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/sync_native.py @@ -0,0 +1,25 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class SyncNativeAccounts(typing.TypedDict): + account: Pubkey + + +def sync_native( + accounts: SyncNativeAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x9b\xdb$$\xef\x80\x15A" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/thaw_account.py b/basics/anchorpy-main/tests/client-gen/token/instructions/thaw_account.py new file mode 100644 index 0000000..fab2d9c --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/thaw_account.py @@ -0,0 +1,29 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class ThawAccountAccounts(typing.TypedDict): + account: Pubkey + mint: Pubkey + owner: Pubkey + + +def thaw_account( + accounts: ThawAccountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["account"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["owner"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"s\x98O\xd5\xd5\xa9\xb8#" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/transfer.py b/basics/anchorpy-main/tests/client-gen/token/instructions/transfer.py new file mode 100644 index 0000000..a24edfb --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/transfer.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class TransferArgs(typing.TypedDict): + amount: int + + +layout = borsh.CStruct("amount" / borsh.U64) + + +class TransferAccounts(typing.TypedDict): + source: Pubkey + destination: Pubkey + authority: Pubkey + + +def transfer( + args: TransferArgs, + accounts: TransferAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["source"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["destination"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["authority"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xa34\xc8\xe7\x8c\x03E\xba" + encoded_args = layout.build( + { + "amount": args["amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/transfer_checked.py b/basics/anchorpy-main/tests/client-gen/token/instructions/transfer_checked.py new file mode 100644 index 0000000..429e631 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/transfer_checked.py @@ -0,0 +1,46 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class TransferCheckedArgs(typing.TypedDict): + amount: int + decimals: int + + +layout = borsh.CStruct("amount" / borsh.U64, "decimals" / borsh.U8) + + +class TransferCheckedAccounts(typing.TypedDict): + source: Pubkey + mint: Pubkey + destination: Pubkey + authority: Pubkey + + +def transfer_checked( + args: TransferCheckedArgs, + accounts: TransferCheckedAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["source"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False), + AccountMeta(pubkey=accounts["destination"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["authority"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"w\xfa\xca\x18\xfd\x87\xf4y" + encoded_args = layout.build( + { + "amount": args["amount"], + "decimals": args["decimals"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/instructions/ui_amount_to_amount.py b/basics/anchorpy-main/tests/client-gen/token/instructions/ui_amount_to_amount.py new file mode 100644 index 0000000..bc72ac8 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/instructions/ui_amount_to_amount.py @@ -0,0 +1,38 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class UiAmountToAmountArgs(typing.TypedDict): + ui_amount: str + + +layout = borsh.CStruct(borsh.String) + + +class UiAmountToAmountAccounts(typing.TypedDict): + mint: Pubkey + + +def ui_amount_to_amount( + args: UiAmountToAmountArgs, + accounts: UiAmountToAmountAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["mint"], is_signer=False, is_writable=False) + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xad\xf3@\x04g\x1f84" + encoded_args = layout.build( + { + "ui_amount": args["ui_amount"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/tests/client-gen/token/program_id.py b/basics/anchorpy-main/tests/client-gen/token/program_id.py new file mode 100644 index 0000000..6f3e9ff --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/program_id.py @@ -0,0 +1,3 @@ +from solders.pubkey import Pubkey + +PROGRAM_ID = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") diff --git a/basics/anchorpy-main/tests/client-gen/token/types/__init__.py b/basics/anchorpy-main/tests/client-gen/token/types/__init__.py new file mode 100644 index 0000000..a0a21a6 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/types/__init__.py @@ -0,0 +1,5 @@ +import typing +from . import account_state +from .account_state import AccountStateKind, AccountStateJSON +from . import authority_type +from .authority_type import AuthorityTypeKind, AuthorityTypeJSON diff --git a/basics/anchorpy-main/tests/client-gen/token/types/account_state.py b/basics/anchorpy-main/tests/client-gen/token/types/account_state.py new file mode 100644 index 0000000..29e8464 --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/types/account_state.py @@ -0,0 +1,105 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from anchorpy.borsh_extension import EnumForCodegen +import borsh_construct as borsh + + +class UninitializedJSON(typing.TypedDict): + kind: typing.Literal["Uninitialized"] + + +class InitializedJSON(typing.TypedDict): + kind: typing.Literal["Initialized"] + + +class FrozenJSON(typing.TypedDict): + kind: typing.Literal["Frozen"] + + +@dataclass +class Uninitialized: + discriminator: typing.ClassVar = 0 + kind: typing.ClassVar = "Uninitialized" + + @classmethod + def to_json(cls) -> UninitializedJSON: + return UninitializedJSON( + kind="Uninitialized", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "Uninitialized": {}, + } + + +@dataclass +class Initialized: + discriminator: typing.ClassVar = 1 + kind: typing.ClassVar = "Initialized" + + @classmethod + def to_json(cls) -> InitializedJSON: + return InitializedJSON( + kind="Initialized", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "Initialized": {}, + } + + +@dataclass +class Frozen: + discriminator: typing.ClassVar = 2 + kind: typing.ClassVar = "Frozen" + + @classmethod + def to_json(cls) -> FrozenJSON: + return FrozenJSON( + kind="Frozen", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "Frozen": {}, + } + + +AccountStateKind = typing.Union[Uninitialized, Initialized, Frozen] +AccountStateJSON = typing.Union[UninitializedJSON, InitializedJSON, FrozenJSON] + + +def from_decoded(obj: dict) -> AccountStateKind: + if not isinstance(obj, dict): + raise ValueError("Invalid enum object") + if "Uninitialized" in obj: + return Uninitialized() + if "Initialized" in obj: + return Initialized() + if "Frozen" in obj: + return Frozen() + raise ValueError("Invalid enum object") + + +def from_json(obj: AccountStateJSON) -> AccountStateKind: + if obj["kind"] == "Uninitialized": + return Uninitialized() + if obj["kind"] == "Initialized": + return Initialized() + if obj["kind"] == "Frozen": + return Frozen() + kind = obj["kind"] + raise ValueError(f"Unrecognized enum kind: {kind}") + + +layout = EnumForCodegen( + "Uninitialized" / borsh.CStruct(), + "Initialized" / borsh.CStruct(), + "Frozen" / borsh.CStruct(), +) diff --git a/basics/anchorpy-main/tests/client-gen/token/types/authority_type.py b/basics/anchorpy-main/tests/client-gen/token/types/authority_type.py new file mode 100644 index 0000000..b77138e --- /dev/null +++ b/basics/anchorpy-main/tests/client-gen/token/types/authority_type.py @@ -0,0 +1,134 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from anchorpy.borsh_extension import EnumForCodegen +import borsh_construct as borsh + + +class MintTokensJSON(typing.TypedDict): + kind: typing.Literal["MintTokens"] + + +class FreezeAccountJSON(typing.TypedDict): + kind: typing.Literal["FreezeAccount"] + + +class AccountOwnerJSON(typing.TypedDict): + kind: typing.Literal["AccountOwner"] + + +class CloseAccountJSON(typing.TypedDict): + kind: typing.Literal["CloseAccount"] + + +@dataclass +class MintTokens: + discriminator: typing.ClassVar = 0 + kind: typing.ClassVar = "MintTokens" + + @classmethod + def to_json(cls) -> MintTokensJSON: + return MintTokensJSON( + kind="MintTokens", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "MintTokens": {}, + } + + +@dataclass +class FreezeAccount: + discriminator: typing.ClassVar = 1 + kind: typing.ClassVar = "FreezeAccount" + + @classmethod + def to_json(cls) -> FreezeAccountJSON: + return FreezeAccountJSON( + kind="FreezeAccount", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "FreezeAccount": {}, + } + + +@dataclass +class AccountOwner: + discriminator: typing.ClassVar = 2 + kind: typing.ClassVar = "AccountOwner" + + @classmethod + def to_json(cls) -> AccountOwnerJSON: + return AccountOwnerJSON( + kind="AccountOwner", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "AccountOwner": {}, + } + + +@dataclass +class CloseAccount: + discriminator: typing.ClassVar = 3 + kind: typing.ClassVar = "CloseAccount" + + @classmethod + def to_json(cls) -> CloseAccountJSON: + return CloseAccountJSON( + kind="CloseAccount", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "CloseAccount": {}, + } + + +AuthorityTypeKind = typing.Union[MintTokens, FreezeAccount, AccountOwner, CloseAccount] +AuthorityTypeJSON = typing.Union[ + MintTokensJSON, FreezeAccountJSON, AccountOwnerJSON, CloseAccountJSON +] + + +def from_decoded(obj: dict) -> AuthorityTypeKind: + if not isinstance(obj, dict): + raise ValueError("Invalid enum object") + if "MintTokens" in obj: + return MintTokens() + if "FreezeAccount" in obj: + return FreezeAccount() + if "AccountOwner" in obj: + return AccountOwner() + if "CloseAccount" in obj: + return CloseAccount() + raise ValueError("Invalid enum object") + + +def from_json(obj: AuthorityTypeJSON) -> AuthorityTypeKind: + if obj["kind"] == "MintTokens": + return MintTokens() + if obj["kind"] == "FreezeAccount": + return FreezeAccount() + if obj["kind"] == "AccountOwner": + return AccountOwner() + if obj["kind"] == "CloseAccount": + return CloseAccount() + kind = obj["kind"] + raise ValueError(f"Unrecognized enum kind: {kind}") + + +layout = EnumForCodegen( + "MintTokens" / borsh.CStruct(), + "FreezeAccount" / borsh.CStruct(), + "AccountOwner" / borsh.CStruct(), + "CloseAccount" / borsh.CStruct(), +) From d1e9ed7842121c4f06cc638a26c34b4bab618d5a Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:35:51 +0530 Subject: [PATCH 18/19] Delete basics/anchorpy-main/tests/client-gen/aq --- basics/anchorpy-main/tests/client-gen/aq | 1 - 1 file changed, 1 deletion(-) delete mode 100644 basics/anchorpy-main/tests/client-gen/aq diff --git a/basics/anchorpy-main/tests/client-gen/aq b/basics/anchorpy-main/tests/client-gen/aq deleted file mode 100644 index 8b13789..0000000 --- a/basics/anchorpy-main/tests/client-gen/aq +++ /dev/null @@ -1 +0,0 @@ - From 5d67cb64878d8695533a512ad58ae6ca48318aef Mon Sep 17 00:00:00 2001 From: Rohit Goyal <66769571+rohit-goyal@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:38:56 +0530 Subject: [PATCH 19/19] Add files via upload --- basics/anchorpy-main/docs/cli/index.md | 80 +++ basics/anchorpy-main/docs/clientgen/index.md | 206 ++++++ basics/anchorpy-main/docs/css/custom.css | 25 + .../anchorpy-main/docs/css/mkdocstrings.css | 6 + basics/anchorpy-main/docs/css/termynal.css | 108 +++ .../docs/dynamic_client/api_reference.md | 27 + .../comparison_with_anchor_ts.md | 36 + .../docs/dynamic_client/examples.md | 85 +++ .../docs/dynamic_client/index.md | 165 +++++ basics/anchorpy-main/docs/img/anchor.svg | 1 + basics/anchorpy-main/docs/img/logo.png | Bin 0 -> 502304 bytes basics/anchorpy-main/docs/index.md | 23 + basics/anchorpy-main/docs/js/custom.js | 105 +++ basics/anchorpy-main/docs/js/termynal.js | 264 ++++++++ basics/anchorpy-main/docs/testing/index.md | 202 ++++++ .../examples/client-gen/basic_2/__init__.py | 0 .../client-gen/basic_2/accounts/__init__.py | 1 + .../client-gen/basic_2/accounts/counter.py | 87 +++ .../client-gen/basic_2/errors/__init__.py | 26 + .../client-gen/basic_2/errors/anchor.py | 590 ++++++++++++++++ .../basic_2/instructions/__init__.py | 2 + .../client-gen/basic_2/instructions/create.py | 41 ++ .../basic_2/instructions/increment.py | 27 + .../examples/client-gen/basic_2/program_id.py | 3 + .../examples/client-gen/tictactoe/__init__.py | 0 .../client-gen/tictactoe/accounts/__init__.py | 1 + .../client-gen/tictactoe/accounts/game.py | 135 ++++ .../client-gen/tictactoe/errors/__init__.py | 29 + .../client-gen/tictactoe/errors/anchor.py | 590 ++++++++++++++++ .../client-gen/tictactoe/errors/custom.py | 66 ++ .../tictactoe/instructions/__init__.py | 2 + .../client-gen/tictactoe/instructions/play.py | 41 ++ .../tictactoe/instructions/setup_game.py | 43 ++ .../client-gen/tictactoe/program_id.py | 3 + .../client-gen/tictactoe/types/__init__.py | 7 + .../client-gen/tictactoe/types/game_state.py | 129 ++++ .../client-gen/tictactoe/types/sign.py | 75 +++ .../client-gen/tictactoe/types/tile.py | 31 + basics/anchorpy-main/overrides/main.html | 5 + basics/anchorpy-main/src/anchorpy/__init__.py | 52 ++ .../src/anchorpy/borsh_extension.py | 121 ++++ basics/anchorpy-main/src/anchorpy/cli.py | 142 ++++ .../src/anchorpy/clientgen/__init__.py | 0 .../src/anchorpy/clientgen/accounts.py | 303 +++++++++ .../src/anchorpy/clientgen/common.py | 636 ++++++++++++++++++ .../src/anchorpy/clientgen/errors.py | 245 +++++++ .../src/anchorpy/clientgen/genpy_extension.py | 239 +++++++ .../src/anchorpy/clientgen/instructions.py | 363 ++++++++++ .../src/anchorpy/clientgen/program_id.py | 16 + .../src/anchorpy/clientgen/types.py | 623 +++++++++++++++++ .../src/anchorpy/coder/__init__.py | 1 + .../src/anchorpy/coder/accounts.py | 73 ++ .../anchorpy-main/src/anchorpy/coder/coder.py | 20 + .../src/anchorpy/coder/common.py | 125 ++++ .../anchorpy-main/src/anchorpy/coder/event.py | 82 +++ .../anchorpy-main/src/anchorpy/coder/idl.py | 325 +++++++++ .../src/anchorpy/coder/instruction.py | 98 +++ basics/anchorpy-main/src/anchorpy/error.py | 340 ++++++++++ basics/anchorpy-main/src/anchorpy/idl.py | 46 ++ .../src/anchorpy/program/__init__.py | 1 + .../src/anchorpy/program/common.py | 90 +++ .../src/anchorpy/program/context.py | 69 ++ .../src/anchorpy/program/core.py | 264 ++++++++ .../src/anchorpy/program/event.py | 166 +++++ .../anchorpy/program/namespace/__init__.py | 1 + .../src/anchorpy/program/namespace/account.py | 231 +++++++ .../anchorpy/program/namespace/instruction.py | 127 ++++ .../src/anchorpy/program/namespace/methods.py | 158 +++++ .../src/anchorpy/program/namespace/rpc.py | 71 ++ .../anchorpy/program/namespace/simulate.py | 94 +++ .../anchorpy/program/namespace/transaction.py | 65 ++ .../src/anchorpy/program/namespace/types.py | 27 + basics/anchorpy-main/src/anchorpy/provider.py | 219 ++++++ basics/anchorpy-main/src/anchorpy/py.typed | 0 .../src/anchorpy/pytest_plugin.py | 296 ++++++++ basics/anchorpy-main/src/anchorpy/template.py | 31 + .../src/anchorpy/utils/__init__.py | 4 + .../anchorpy-main/src/anchorpy/utils/rpc.py | 157 +++++ .../anchorpy-main/src/anchorpy/utils/token.py | 318 +++++++++ .../anchorpy-main/src/anchorpy/workspace.py | 52 ++ 80 files changed, 9558 insertions(+) create mode 100644 basics/anchorpy-main/docs/cli/index.md create mode 100644 basics/anchorpy-main/docs/clientgen/index.md create mode 100644 basics/anchorpy-main/docs/css/custom.css create mode 100644 basics/anchorpy-main/docs/css/mkdocstrings.css create mode 100644 basics/anchorpy-main/docs/css/termynal.css create mode 100644 basics/anchorpy-main/docs/dynamic_client/api_reference.md create mode 100644 basics/anchorpy-main/docs/dynamic_client/comparison_with_anchor_ts.md create mode 100644 basics/anchorpy-main/docs/dynamic_client/examples.md create mode 100644 basics/anchorpy-main/docs/dynamic_client/index.md create mode 100644 basics/anchorpy-main/docs/img/anchor.svg create mode 100644 basics/anchorpy-main/docs/img/logo.png create mode 100644 basics/anchorpy-main/docs/index.md create mode 100644 basics/anchorpy-main/docs/js/custom.js create mode 100644 basics/anchorpy-main/docs/js/termynal.js create mode 100644 basics/anchorpy-main/docs/testing/index.md create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/accounts/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/accounts/counter.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/errors/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/errors/anchor.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/instructions/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/instructions/create.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/instructions/increment.py create mode 100644 basics/anchorpy-main/examples/client-gen/basic_2/program_id.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/accounts/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/accounts/game.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/errors/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/errors/anchor.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/errors/custom.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/instructions/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/instructions/play.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/instructions/setup_game.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/program_id.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/types/__init__.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/types/game_state.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/types/sign.py create mode 100644 basics/anchorpy-main/examples/client-gen/tictactoe/types/tile.py create mode 100644 basics/anchorpy-main/overrides/main.html create mode 100644 basics/anchorpy-main/src/anchorpy/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/borsh_extension.py create mode 100644 basics/anchorpy-main/src/anchorpy/cli.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/accounts.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/common.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/errors.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/genpy_extension.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/instructions.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/program_id.py create mode 100644 basics/anchorpy-main/src/anchorpy/clientgen/types.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/accounts.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/coder.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/common.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/event.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/idl.py create mode 100644 basics/anchorpy-main/src/anchorpy/coder/instruction.py create mode 100644 basics/anchorpy-main/src/anchorpy/error.py create mode 100644 basics/anchorpy-main/src/anchorpy/idl.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/common.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/context.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/core.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/event.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/account.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/instruction.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/methods.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/rpc.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/simulate.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/transaction.py create mode 100644 basics/anchorpy-main/src/anchorpy/program/namespace/types.py create mode 100644 basics/anchorpy-main/src/anchorpy/provider.py create mode 100644 basics/anchorpy-main/src/anchorpy/py.typed create mode 100644 basics/anchorpy-main/src/anchorpy/pytest_plugin.py create mode 100644 basics/anchorpy-main/src/anchorpy/template.py create mode 100644 basics/anchorpy-main/src/anchorpy/utils/__init__.py create mode 100644 basics/anchorpy-main/src/anchorpy/utils/rpc.py create mode 100644 basics/anchorpy-main/src/anchorpy/utils/token.py create mode 100644 basics/anchorpy-main/src/anchorpy/workspace.py diff --git a/basics/anchorpy-main/docs/cli/index.md b/basics/anchorpy-main/docs/cli/index.md new file mode 100644 index 0000000..d32f867 --- /dev/null +++ b/basics/anchorpy-main/docs/cli/index.md @@ -0,0 +1,80 @@ +# AnchorPy CLI + +AnchorPy comes with a CLI to make your life easier when using Python with Anchor. + + +```console +$ anchorpy --help +Usage: anchorpy [OPTIONS] COMMAND [ARGS]... + + AnchorPy CLI. + +Options: + --install-completion [bash|zsh|fish|powershell|pwsh] + Install completion for the specified shell. + --show-completion [bash|zsh|fish|powershell|pwsh] + Show completion for the specified shell, to + copy it or customize the installation. + --help Show this message and exit. + +Commands: + init Create a basic Python test file for an Anchor program. + shell Start IPython shell with AnchorPy workspace object initialized. +``` + +## Commands + +### Init + +```console +$ anchorpy init --help +Usage: anchorpy init [OPTIONS] PROGRAM_NAME + + Create a basic Python test file for an Anchor program. + + This does not replace `anchor init`, but rather should be run after it. + + The test file will live at `tests/test_$PROGRAM_NAME.py`. + +Arguments: + PROGRAM_NAME The name of the Anchor program. [required] + +Options: + --help Show this message and exit. +``` + +### Shell + +```console +$ anchorpy shell --help +Usage: anchorpy shell [OPTIONS] + + Start IPython shell with AnchorPy workspace object initialized. + + Note that you should run `anchor localnet` before `anchorpy shell`. + +Options: + --help Show this message and exit. +``` + +#### Example + +
+ +```console +$ cd anchor/examples/tutorial/basic-0 && anchorpy shell +Python 3.9.1 (default, Dec 11 2020, 14:32:07) +Type 'copyright', 'credits' or 'license' for more information +IPython 7.30.1 -- An enhanced Interactive Python. Type '?' for help. + +Hint: type `workspace` to see the Anchor workspace object. + +# In [1]:$ await workspace["basic_0"].rpc["initialize"]() +Out[1]: '2q1Z8BcsSBikMLEFoeFGhUukfsNYLJx7y33rMZ57Eh4gAHJxpJ9oP9b9aFyrizh9wcuiVtAAxvmBifCXdqWeNLor' +``` + +
+ +### Client-gen + +See [Client Generator](../clientgen) \ No newline at end of file diff --git a/basics/anchorpy-main/docs/clientgen/index.md b/basics/anchorpy-main/docs/clientgen/index.md new file mode 100644 index 0000000..d44f8ce --- /dev/null +++ b/basics/anchorpy-main/docs/clientgen/index.md @@ -0,0 +1,206 @@ +# Client Generator + +The `anchorpy client-gen` command generates a Python client based on an Anchor IDL. This client provides functions for +generating instructions and class definitions for fetching and serializing accounts. The output is very similar +to the Typescript code produced by [anchor-client-gen](https://github.com/kklas/anchor-client-gen). + +## Features + +- Fully typed. +- Supports all Anchor field types. +- Makes enums and complex types easy to work with. +- Generates error classes for each error. +- Provides `to_json` and `from_json` utility functions for types and accounts. + +!!! note + It is recommended to use the generated client instead of the dynamic client where possible, as it is easier to + work with and comes with proper type hints. + +## Usage + +```console +$ anchorpy client-gen --help +Usage: anchorpy client-gen [OPTIONS] IDL OUT + + Generate Python client code from the specified anchor IDL. + +Arguments: + IDL Anchor IDL file path [required] + OUT Output directory. [required] + +Options: + --program-id TEXT Optional program ID to be included in the code + --help Show this message and exit. + +``` + +## Example + +
+ +```console +$ anchorpy client-gen path/to/idl.json ./my_client +generating package... +generating program_id.py... +generating errors.py... +generating instructions... +generating types... +generating accounts... +``` + +
+ +This will generate code to `./my_client`: + +``` +. +├── accounts +│ ├── foo_account.py +│ └── __init__.py +├── instructions +│ ├── some_instruction.py +│ ├── other_instruction.py +│ └── __init__.py +├── types +│ ├── bar_struct.py +│ ├── baz_enum.py +│ └── __init__.py +├── errors +│ ├── anchor.py +│ ├── custom.py +│ └── __init__.py +└── program_id.py +``` + +## Using the generated client + +### Instructions + +```python +from solana.transaction import Transaction +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from anchorpy import Provider +from my_client.instructions import some_instruction + + # call an instruction +foo_account = Keypair() + +ix = some_instruction({ + "foo_param": "...", + "bar_param": "...", + ... +}, { + "foo_account": foo_account.pubkey(), # signer + "bar_account": Pubkey("..."), + ... +}) +tx = Transaction().add(ix) + +provider = Provider.local() + +await provider.send(tx, [payer, foo_account]) +``` + +### Accounts + +```python +from solders.pubkey import Pubkey +from my_client.accounts import FooAccount + +# fetch an account +addr = Pubkey("...") + +acc = await FooAccount.fetch(connection, addr) +if acc is None: + # the fetch method returns null when the account is uninitialized + raise ValueError("account not found") + + +# convert to a JSON object +obj = acc.to_json() +print(obj) + +# load from JSON +acc_from_json = FooAccount.from_json(obj) +``` + +### Types + +```python +# structs + +from my_client.types import BarStruct + +bar_struct = BarStruct( + some_field="...", + other_field="...", +) + +print(bar_struct.to_json()) +``` + +```python +# enums + +from my_client.types import baz_enum + +tuple_enum = baz_enum.SomeTupleKind((True, False, "some value")) +struct_enum = baz_enum.SomeStructKind({ + "field1": "...", + "field2": "...", +}) +disc_enum = baz_enum.SomeDiscriminantKind() + +print(tuple_enum.toJSON(), struct_enum.toJSON(), disc_enum.toJSON()) +``` + +```python +# types are used as arguments in instruction calls (where needed): +ix = some_instruction({ + "some_struct_field": bar_struct, + "some_enum_field": tuple_enum, + # ... +}, { + # accounts + # ... +}) + +# in case of struct fields, it's also possible to pass them as objects: +ix = some_instruction({ + "some_struct_field": { + "some_field": "...", + "other_field": "...", + }, + # ..., +}, { + # accounts + # ... +}) +``` + +### Errors + +```python +from solana.rpc.core import RPCException +from my_client.errors import from_tx_error +from my_client.errors.custom import SomeCustomError + +try: + await provider.send(tx, [payer]) +except RPCException as exc: + parsed = from_tx_error(exc) + raise parsed from exc +``` + +## Program ID + +The client generator pulls the program ID from: + +- the input IDL +- the `--program-id` flag + +If the IDL doesn't contain the program ID then you will need to pass it via the `--program-id` flag. + +This program ID is then written into the `program_id.py` file. + diff --git a/basics/anchorpy-main/docs/css/custom.css b/basics/anchorpy-main/docs/css/custom.css new file mode 100644 index 0000000..574fc63 --- /dev/null +++ b/basics/anchorpy-main/docs/css/custom.css @@ -0,0 +1,25 @@ +.termynal-comment { + color: #4a968f; + font-style: italic; + display: block; +} + +.termy [data-termynal] { + white-space: pre-wrap; + font-size: 14px; + overflow-wrap: break-word; +} + +a.external-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0[↪]"; +} + +a.internal-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0↪"; +} \ No newline at end of file diff --git a/basics/anchorpy-main/docs/css/mkdocstrings.css b/basics/anchorpy-main/docs/css/mkdocstrings.css new file mode 100644 index 0000000..024fe6e --- /dev/null +++ b/basics/anchorpy-main/docs/css/mkdocstrings.css @@ -0,0 +1,6 @@ +/* Indentation. */ +div.doc-contents:not(.first) { + padding-left: 25px; + border-left: 4px solid rgba(230, 230, 230); + margin-bottom: 80px; + } diff --git a/basics/anchorpy-main/docs/css/termynal.css b/basics/anchorpy-main/docs/css/termynal.css new file mode 100644 index 0000000..dfe3caa --- /dev/null +++ b/basics/anchorpy-main/docs/css/termynal.css @@ -0,0 +1,108 @@ +/** + * termynal.js + * + * @author Ines Montani + * @version 0.0.1 + * @license MIT + */ + + :root { + --color-bg: #252a33; + --color-text: #eee; + --color-text-subtle: #a2a2a2; +} + +[data-termynal] { + width: 750px; + max-width: 100%; + background: var(--color-bg); + color: var(--color-text); + font-size: 18px; + /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ + font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; + border-radius: 4px; + padding: 75px 45px 35px; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +[data-termynal]:before { + content: ''; + position: absolute; + top: 15px; + left: 15px; + display: inline-block; + width: 15px; + height: 15px; + border-radius: 50%; + /* A little hack to display the window buttons in one pseudo element. */ + background: #d9515d; + -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; + box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; +} + +[data-termynal]:after { + content: 'bash'; + position: absolute; + color: var(--color-text-subtle); + top: 5px; + left: 0; + width: 100%; + text-align: center; +} + +a[data-terminal-control] { + text-align: right; + display: block; + color: #aebbff; +} + +[data-ty] { + display: block; + line-height: 2; +} + +[data-ty]:before { + /* Set up defaults and ensure empty lines are displayed. */ + content: ''; + display: inline-block; + vertical-align: middle; +} + +[data-ty="input"]:before, +[data-ty-prompt]:before { + margin-right: 0.75em; + color: var(--color-text-subtle); +} + +[data-ty="input"]:before { + content: '$'; +} + +[data-ty][data-ty-prompt]:before { + content: attr(data-ty-prompt); +} + +[data-ty-cursor]:after { + content: attr(data-ty-cursor); + font-family: monospace; + margin-left: 0.5em; + -webkit-animation: blink 1s infinite; + animation: blink 1s infinite; +} + + +/* Cursor animation */ + +@-webkit-keyframes blink { + 50% { + opacity: 0; + } +} + +@keyframes blink { + 50% { + opacity: 0; + } +} diff --git a/basics/anchorpy-main/docs/dynamic_client/api_reference.md b/basics/anchorpy-main/docs/dynamic_client/api_reference.md new file mode 100644 index 0000000..261b98d --- /dev/null +++ b/basics/anchorpy-main/docs/dynamic_client/api_reference.md @@ -0,0 +1,27 @@ +# API Reference + + +:::anchorpy.Program +:::anchorpy.Provider +:::anchorpy.Context +:::anchorpy.create_workspace +:::anchorpy.close_workspace +:::anchorpy.bankrun_fixture +:::anchorpy.workspace_fixture +:::anchorpy.localnet_fixture +:::anchorpy.Wallet +:::anchorpy.Coder +:::anchorpy.InstructionCoder +:::anchorpy.EventCoder +:::anchorpy.AccountsCoder +:::anchorpy.NamedInstruction +:::anchorpy.IdlProgramAccount +:::anchorpy.Event +:::anchorpy.translate_address +:::anchorpy.validate_accounts +:::anchorpy.AccountClient +:::anchorpy.ProgramAccount +:::anchorpy.EventParser +:::anchorpy.SimulateResponse +:::anchorpy.error +:::anchorpy.utils diff --git a/basics/anchorpy-main/docs/dynamic_client/comparison_with_anchor_ts.md b/basics/anchorpy-main/docs/dynamic_client/comparison_with_anchor_ts.md new file mode 100644 index 0000000..db3e220 --- /dev/null +++ b/basics/anchorpy-main/docs/dynamic_client/comparison_with_anchor_ts.md @@ -0,0 +1,36 @@ +# Comparison with Typescript + +While AnchorPy is quite similar to the Anchor Typescript client, +there are some differences: + +## Dictionaries instead of objects + +AnchorPy tends to use dictionaries, so it uses `[key]` in some places where +`anchor-ts` would use `.key`. +For example, AnchorPy uses `workspace["basic_1"]` instead of `workspace.basic_1`, +and `program.rpc["initialize"]()` instead of `program.rpc.initialize()` + +## Explicit `Context` object + +AnchorPy uses a `Context` dataclass and has a `ctx` keyword argument when +calling `.rpc` functions, whereas Typescript is a bit less structured. + +We call `program.rpc["my_func"](ctx=Context({"my_account": my_account}))` +instead of `program.rpc["my_func"]({"my_account": my_account})` + +## snake_case 🐍 instead of camelCase 🐪 + +AnchorPy uses more `snake_case` to match Rust and be Pythonic. +Specifically, the following names are snake-case in AnchorPy: + +- Workspaces: `workspace["puppet_master"]` instead of `workspace["puppetMaster"]` +- Instructions: `program.rpc["my_func"]` (and `program.instruction["my_func"]`) instead of +`program.rpc["myFunc"]`. +- Accounts in the `ctx` arg: `{"my_account": my_account}` instead of `{"myAccount": my_account}` +- Fields in user-defined types: `program.type["TransactionAccount"](is_writable=True)` instead of +`program.type["TransactionAccount"](isWritable=True)` + +## `program.type` namespace for user-defined types + +The AnchorPy `Program` object has a `.type` attribute for instantiating user-defined types. This is not present in +the Typescript client. See the [examples](examples.md) for more on this. diff --git a/basics/anchorpy-main/docs/dynamic_client/examples.md b/basics/anchorpy-main/docs/dynamic_client/examples.md new file mode 100644 index 0000000..b91b806 --- /dev/null +++ b/basics/anchorpy-main/docs/dynamic_client/examples.md @@ -0,0 +1,85 @@ +# Examples + +Here are some of the other things you can do with AnchorPy: + +## Loading a Program from an on-chain IDL + +If a program's IDL is stored on-chain, you can use it +to initialize a program object using `Program.at`. + +````python +import asyncio +from solana.rpc.async_api import AsyncClient +from solders.pubkey import Pubkey +from anchorpy import Program, Provider, Wallet + + +async def main(): + client = AsyncClient("https://api.mainnet-beta.solana.com/") + provider = Provider(client, Wallet.local()) + # load the Serum Swap Program (not the Serum dex itself). + program_id = Pubkey.from_string("22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD") + program = await Program.at( + program_id, provider + ) + print(program.idl.name) # swap + await program.close() + + +asyncio.run(main()) + + +```` + +## Instantiating user-defined types with `program.type` + +### Enums + +Suppose we have an instruction that expects an Enum called `Side`, +with variants `Buy` and `Sell`. The `Program` object has a `.type` +namespace to make it easy to use this enum: + +````python +await program.rpc["my_func"](program.type["Side"].Buy()) + +```` +See [test_token_proxy.py](https://github.com/kevinheavey/anchorpy/blob/main/tests/test_token_proxy.py) +for a more concrete example. + +### Structs + +`.type` also allows us to build structs defined in the IDL. +See this snippet from [test_multisig.py](https://github.com/kevinheavey/anchorpy/blob/main/tests/test_multisig.py): + +````python +program.type["TransactionAccount"]( + pubkey=multisig.pubkey(), + is_writable=True, + is_signer=False, +) + +```` + +## Bulk-fetching data with `.fetch_multiple` + +You can use `.fetch_multiple` to get deserialized account data +for many accounts at once. Look at this example from `test_misc.py`: + +````python +n_accounts = 200 +pubkeys = [initialized_keypair.pubkey()] * n_accounts # noqa: WPS435 +data_accounts = await program.account["Data"].fetch_multiple( + pubkeys, batch_size=2 +) + +```` + +The above example fetches data for the same pubkey 200 times which is +not very interesting, but it could just as easily be fetching 200 +different accounts. The `.fetch_multiple` method uses async batch RPC requests +and `getMultipleAccounts` so it's quite efficient. + +!!! warning + Be mindful of your RPC provider when fetching data, and plan out how + many requests you'll end up sending to the RPC node. You can reliably + fetch around 300 public keys in one HTTP request. diff --git a/basics/anchorpy-main/docs/dynamic_client/index.md b/basics/anchorpy-main/docs/dynamic_client/index.md new file mode 100644 index 0000000..517022a --- /dev/null +++ b/basics/anchorpy-main/docs/dynamic_client/index.md @@ -0,0 +1,165 @@ +# Tutorial + + +!!! note + It is recommended to use the generated client instead of the dynamic client where possible, as it is easier to + work with and comes with proper type hints. + +This tutorial takes the JS snippets from the official Anchor tutorial +and shows how to achieve the same thing using AnchorPy. + +## [A Minimal Example](https://project-serum.github.io/anchor/tutorials/tutorial-0.html) +This section covers the `basic-0` tutorial: +### [Generating a Client](https://project-serum.github.io/anchor/tutorials/tutorial-0.html#generating-a-client) +Here is how we generate a client from an IDL and use it to interact with a smart contract. + + +=== "Python" + ````python + from pathlib import Path + import asyncio + import json + from solders.pubkey import Pubkey + from anchorpy import Idl, Program + + async def main(): + # Read the generated IDL. + with Path("target/idl/basic_0.json").open() as f: + raw_idl = f.read() + idl = Idl.from_json(raw_idl) + # Address of the deployed program. + program_id = Pubkey.from_string("") + # Generate the program client from IDL. + async with Program(idl, program_id) as program: + # Execute the RPC. + await program.rpc["initialize"]() + # If we don't use the context manager, we need to + # close the underlying http client, otherwise we get warnings. + # await program.close() + + asyncio.run(main()) + + ```` + +=== "JS" + ````javascript + // Read the generated IDL. + const idl = JSON.parse(require('fs').readFileSync('./target/idl/basic_0.json', 'utf8')); + + // Address of the deployed program. + const programId = new anchor.web3.Pubkey(''); + + // Generate the program client from IDL. + const program = new anchor.Program(idl, programId); + + // Execute the RPC. + await program.rpc.initialize(); + + ```` + +Note the differences between Python and JS here: + +- We call `program.rpc["initialize"]()` instead of `program.rpc.initialize()` +- We call `program.close()` to close the HTTP connection. + +### [Workspaces](https://project-serum.github.io/anchor/tutorials/tutorial-0.html#workspaces) + +Here is how workspaces look in AnchorPy: + + +=== "Python" + ````python + import asyncio + from anchorpy import create_workspace, close_workspace + + async def main(): + # Read the deployed program from the workspace. + workspace = create_workspace() + program = workspace["basic_0"] + # Execute the RPC. + await program.rpc["initialize"]() + # Close all HTTP clients in the workspace, otherwise we get warnings. + await close_workspace(workspace) + + asyncio.run(main()) + + ```` + +=== "JS" + ````javascript + // Read the deployed program from the workspace. + const program = anchor.workspace.Basic0; + + // Execute the RPC. + await program.rpc.initialize(); + + ```` + +Note the differences between Python and JS: + +- Workspace instantiation is explicit: we have to call the `create_workspace` function. + - Note however that AnchorPy provides the `workspace_fixture` factory for convenience. + See the [testing](../testing/index.md) section for more. +- We have a `close_workspace` function that calls `close_program` on all the programs +in the workspace. +- The workspace is called `basic_0` instead of `Basic0`. This is because AnchorPy uses snake case 🐍 +!!! Note + AnchorPy uses the same case convention as Rust, so names should look just like they do in `lib.rs`. + If you're unsure of a name, check `program.idl`: it shows how AnchorPy sees the IDL after parsing + it and converting some cases. + +## [Arguments and Accounts](https://project-serum.github.io/anchor/tutorials/tutorial-1.html) +### [Creating and Initializing Accounts](https://project-serum.github.io/anchor/tutorials/tutorial-1.html#creating-and-initializing-accounts) + +Here is how we call an RPC function with arguments. +As in the main Anchor tutorial, we will use `anchor/tutorial/examples/basic-1`: + +=== "Python" + ````python + import asyncio + from solders.keypair import Keypair + from solders.system_program import ID as SYS_PROGRAM_ID + from anchorpy import create_workspace, close_workspace, Context + + async def main(): + # Read the deployed program from the workspace. + workspace = create_workspace() + # The program to execute. + program = workspace["basic_1"] + # The Account to create. + my_account = Keypair() + # Execute the RPC. + accounts = { + "my_account": my_account.pubkey(), + "user": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID + } + await program.rpc["initialize"](1234, ctx=Context(accounts=accounts, signers=[my_account])) + # Close all HTTP clients in the workspace, otherwise we get warnings. + await close_workspace(workspace) + + asyncio.run(main()) + + ```` + +=== "JS" + ````javascript + // The program to execute. + const program = anchor.workspace.Basic1; + + // The Account to create. + const myAccount = anchor.web3.Keypair(); + + // Create the new account and initialize it with the program. + await program.rpc.initialize(new anchor.BN(1234), { + accounts: { + myAccount: myAccount.Pubkey, + user: provider.wallet.Pubkey, + systemProgram: SystemProgram.programId, + }, + signers: [myAccount], + }); + + ```` + +Note how AnchorPy uses an explicit `Context` object in contrast to TS/JS. diff --git a/basics/anchorpy-main/docs/img/anchor.svg b/basics/anchorpy-main/docs/img/anchor.svg new file mode 100644 index 0000000..aa184c5 --- /dev/null +++ b/basics/anchorpy-main/docs/img/anchor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/basics/anchorpy-main/docs/img/logo.png b/basics/anchorpy-main/docs/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7ba560c369f8421646c05fa05597792ab7db6281 GIT binary patch literal 502304 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
Bu4|KTURp}OF{w5Cs}N)eL5cC5HnK! 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%0D-0huOtYV7-r`8#_t$W~YQ2$;aX4v`WVblV` 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%0A-tyMzjxi7~DEDEyzg`Vdj0s1& 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 z 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 literal 0 HcmV?d00001 diff --git a/basics/anchorpy-main/docs/index.md b/basics/anchorpy-main/docs/index.md new file mode 100644 index 0000000..bb8c424 --- /dev/null +++ b/basics/anchorpy-main/docs/index.md @@ -0,0 +1,23 @@ +# Introduction +
+ +
+ +--- +AnchorPy is the gateway to interacting with [Anchor](https://github.com/project-serum/anchor) programs in Python. +It provides: + +- A [static client generator](clientgen) +- A [dynamic client](dynamic_client) similar to `anchor-ts` +- A [Pytest plugin](testing/#1-pytest-plugin) +- A [CLI](cli) with various utilities for Anchor Python development. + +## Installation (requires Python >= 3.9) + +```shell +pip install anchorpy[cli] +``` + + +!!! note + These docs will assume you've read the [Anchor documentation](https://www.anchor-lang.com/) first. diff --git a/basics/anchorpy-main/docs/js/custom.js b/basics/anchorpy-main/docs/js/custom.js new file mode 100644 index 0000000..45243eb --- /dev/null +++ b/basics/anchorpy-main/docs/js/custom.js @@ -0,0 +1,105 @@ +document.querySelectorAll(".use-termynal").forEach(node => { + node.style.display = "block"; + new Termynal(node, { + lineDelay: 500 + }); +}); +const progressLiteralStart = "---> 100%"; +const promptLiteralStart = "$ "; +const customPromptLiteralStart = "# "; +const termynalActivateClass = "termy"; +let termynals = []; + +function createTermynals() { + document + .querySelectorAll(`.${termynalActivateClass} .highlight`) + .forEach(node => { + const text = node.textContent; + const lines = text.split("\n"); + const useLines = []; + let buffer = []; + function saveBuffer() { + if (buffer.length) { + let isBlankSpace = true; + buffer.forEach(line => { + if (line) { + isBlankSpace = false; + } + }); + dataValue = {}; + if (isBlankSpace) { + dataValue["delay"] = 0; + } + if (buffer[buffer.length - 1] === "") { + // A last single
won't have effect + // so put an additional one + buffer.push(""); + } + const bufferValue = buffer.join("
"); + dataValue["value"] = bufferValue; + useLines.push(dataValue); + buffer = []; + } + } + for (let line of lines) { + if (line === progressLiteralStart) { + saveBuffer(); + useLines.push({ + type: "progress" + }); + } else if (line.startsWith(promptLiteralStart)) { + saveBuffer(); + const value = line.replace(promptLiteralStart, "").trimEnd(); + useLines.push({ + type: "input", + value: value + }); + } else if (line.startsWith("// ")) { + saveBuffer(); + const value = "💬 " + line.replace("// ", "").trimEnd(); + useLines.push({ + value: value, + class: "termynal-comment", + delay: 0 + }); + } else if (line.startsWith(customPromptLiteralStart)) { + saveBuffer(); + const promptStart = line.indexOf(promptLiteralStart); + if (promptStart === -1) { + console.error("Custom prompt found but no end delimiter", line) + } + const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "") + let value = line.slice(promptStart + promptLiteralStart.length); + useLines.push({ + type: "input", + value: value, + prompt: prompt + }); + } else { + buffer.push(line); + } + } + saveBuffer(); + const div = document.createElement("div"); + node.replaceWith(div); + const termynal = new Termynal(div, { + lineData: useLines, + noInit: true, + lineDelay: 500 + }); + termynals.push(termynal); + }); +} + +function loadVisibleTermynals() { + termynals = termynals.filter(termynal => { + if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { + termynal.init(); + return false; + } + return true; + }); +} +window.addEventListener("scroll", loadVisibleTermynals); +createTermynals(); +loadVisibleTermynals(); diff --git a/basics/anchorpy-main/docs/js/termynal.js b/basics/anchorpy-main/docs/js/termynal.js new file mode 100644 index 0000000..a26c870 --- /dev/null +++ b/basics/anchorpy-main/docs/js/termynal.js @@ -0,0 +1,264 @@ +/** + * termynal.js + * A lightweight, modern and extensible animated terminal window, using + * async/await. + * + * @author Ines Montani + * @version 0.0.1 + * @license MIT + */ + +'use strict'; + +/** Generate a terminal widget. */ +class Termynal { + /** + * Construct the widget's settings. + * @param {(string|Node)=} container - Query selector or container element. + * @param {Object=} options - Custom settings. + * @param {string} options.prefix - Prefix to use for data attributes. + * @param {number} options.startDelay - Delay before animation, in ms. + * @param {number} options.typeDelay - Delay between each typed character, in ms. + * @param {number} options.lineDelay - Delay between each line, in ms. + * @param {number} options.progressLength - Number of characters displayed as progress bar. + * @param {string} options.progressChar – Character to use for progress bar, defaults to █. + * @param {number} options.progressPercent - Max percent of progress. + * @param {string} options.cursor – Character to use for cursor, defaults to ▋. + * @param {Object[]} lineData - Dynamically loaded line data objects. + * @param {boolean} options.noInit - Don't initialise the animation. + */ + constructor(container = '#termynal', options = {}) { + this.container = (typeof container === 'string') ? document.querySelector(container) : container; + this.pfx = `data-${options.prefix || 'ty'}`; + this.originalStartDelay = this.startDelay = options.startDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600; + this.originalTypeDelay = this.typeDelay = options.typeDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90; + this.originalLineDelay = this.lineDelay = options.lineDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; + this.progressLength = options.progressLength + || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; + this.progressChar = options.progressChar + || this.container.getAttribute(`${this.pfx}-progressChar`) || '█'; + this.progressPercent = options.progressPercent + || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100; + this.cursor = options.cursor + || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; + this.lineData = this.lineDataToElements(options.lineData || []); + this.loadLines() + if (!options.noInit) this.init() + } + + loadLines() { + // Load all the lines and create the container so that the size is fixed + // Otherwise it would be changing and the user viewport would be constantly + // moving as she/he scrolls + const finish = this.generateFinish() + finish.style.visibility = 'hidden' + this.container.appendChild(finish) + // Appends dynamically loaded lines to existing line elements. + this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); + for (let line of this.lines) { + line.style.visibility = 'hidden' + this.container.appendChild(line) + } + const restart = this.generateRestart() + restart.style.visibility = 'hidden' + this.container.appendChild(restart) + this.container.setAttribute('data-termynal', ''); + } + + /** + * Initialise the widget, get lines, clear container and start animation. + */ + init() { + /** + * Calculates width and height of Termynal container. + * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS. + */ + const containerStyle = getComputedStyle(this.container); + this.container.style.width = containerStyle.width !== '0px' ? + containerStyle.width : undefined; + this.container.style.minHeight = containerStyle.height !== '0px' ? + containerStyle.height : undefined; + + this.container.setAttribute('data-termynal', ''); + this.container.innerHTML = ''; + for (let line of this.lines) { + line.style.visibility = 'visible' + } + this.start(); + } + + /** + * Start the animation and rener the lines depending on their data attributes. + */ + async start() { + this.addFinish() + await this._wait(this.startDelay); + + for (let line of this.lines) { + const type = line.getAttribute(this.pfx); + const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay; + + if (type == 'input') { + line.setAttribute(`${this.pfx}-cursor`, this.cursor); + await this.type(line); + await this._wait(delay); + } + + else if (type == 'progress') { + await this.progress(line); + await this._wait(delay); + } + + else { + this.container.appendChild(line); + await this._wait(delay); + } + + line.removeAttribute(`${this.pfx}-cursor`); + } + this.addRestart() + this.finishElement.style.visibility = 'hidden' + this.lineDelay = this.originalLineDelay + this.typeDelay = this.originalTypeDelay + this.startDelay = this.originalStartDelay + } + + generateRestart() { + const restart = document.createElement('a') + restart.onclick = (e) => { + e.preventDefault() + this.container.innerHTML = '' + this.init() + } + restart.href = '#' + restart.setAttribute('data-terminal-control', '') + restart.innerHTML = "restart ↻" + return restart + } + + generateFinish() { + const finish = document.createElement('a') + finish.onclick = (e) => { + e.preventDefault() + this.lineDelay = 0 + this.typeDelay = 0 + this.startDelay = 0 + } + finish.href = '#' + finish.setAttribute('data-terminal-control', '') + finish.innerHTML = "fast →" + this.finishElement = finish + return finish + } + + addRestart() { + const restart = this.generateRestart() + this.container.appendChild(restart) + } + + addFinish() { + const finish = this.generateFinish() + this.container.appendChild(finish) + } + + /** + * Animate a typed line. + * @param {Node} line - The line element to render. + */ + async type(line) { + const chars = [...line.textContent]; + line.textContent = ''; + this.container.appendChild(line); + + for (let char of chars) { + const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; + await this._wait(delay); + line.textContent += char; + } + } + + /** + * Animate a progress bar. + * @param {Node} line - The line element to render. + */ + async progress(line) { + const progressLength = line.getAttribute(`${this.pfx}-progressLength`) + || this.progressLength; + const progressChar = line.getAttribute(`${this.pfx}-progressChar`) + || this.progressChar; + const chars = progressChar.repeat(progressLength); + const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) + || this.progressPercent; + line.textContent = ''; + this.container.appendChild(line); + + for (let i = 1; i < chars.length + 1; i++) { + await this._wait(this.typeDelay); + const percent = Math.round(i / chars.length * 100); + line.textContent = `${chars.slice(0, i)} ${percent}%`; + if (percent > progressPercent) { + break; + } + } + } + + /** + * Helper function for animation delays, called with `await`. + * @param {number} time - Timeout, in ms. + */ + _wait(time) { + return new Promise(resolve => setTimeout(resolve, time)); + } + + /** + * Converts line data objects into line elements. + * + * @param {Object[]} lineData - Dynamically loaded lines. + * @param {Object} line - Line data object. + * @returns {Element[]} - Array of line elements. + */ + lineDataToElements(lineData) { + return lineData.map(line => { + let div = document.createElement('div'); + div.innerHTML = `${line.value || ''}`; + + return div.firstElementChild; + }); + } + + /** + * Helper function for generating attributes string. + * + * @param {Object} line - Line data object. + * @returns {string} - String of attributes. + */ + _attributes(line) { + let attrs = ''; + for (let prop in line) { + // Custom add class + if (prop === 'class') { + attrs += ` class=${line[prop]} ` + continue + } + if (prop === 'type') { + attrs += `${this.pfx}="${line[prop]}" ` + } else if (prop !== 'value') { + attrs += `${this.pfx}-${prop}="${line[prop]}" ` + } + } + + return attrs; + } +} + +/** +* HTML API: If current script has container(s) specified, initialise Termynal. +*/ +if (document.currentScript.hasAttribute('data-termynal-container')) { + const containers = document.currentScript.getAttribute('data-termynal-container'); + containers.split('|') + .forEach(container => new Termynal(container)) +} diff --git a/basics/anchorpy-main/docs/testing/index.md b/basics/anchorpy-main/docs/testing/index.md new file mode 100644 index 0000000..8d516b5 --- /dev/null +++ b/basics/anchorpy-main/docs/testing/index.md @@ -0,0 +1,202 @@ +# Testing with AnchorPy + +## Approaches + +There are two ways to test Anchor programs using AnchorPy: + +1. Using the AnchorPy Pytest plugin. +2. Using `anchor test` and modifying `Anchor.toml`. + +## 1. Pytest plugin + +AnchorPy provides a `workspace_fixture` function that creates a Pytest fixture. +This fixture runs `anchor localnet` in the project root and shuts down the localnet +when the tests are done. + +With this approach you're just running regular Pytest tests. +This lets you do some things that you can't do with `anchor test`, +like integrating closely with your IDE or opening a debugger when a test fails (`pytest --pdb`). + +Here's how it looks with the `basic-1` tests: + +```python +from pytest import fixture, mark +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + +from anchorpy import Context, Program, workspace_fixture, WorkspaceType + +workspace = workspace_fixture("anchor/examples/tutorial/basic-1") + +# Since our other fixtures have module scope, we need to define +# this event_loop fixture and give it module scope otherwise +# pytest-asyncio will break. + +@fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for each test case.""" + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["basic_1"] + + +@fixture(scope="module") +async def initialized_account(program: Program) -> Keypair: + """Generate a keypair and initialize it.""" + my_account = Keypair() + await program.rpc["initialize"]( + 1234, + ctx=Context( + accounts={ + "my_account": my_account.pubkey(), + "user": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[my_account], + ), + ) + return my_account + + +@mark.asyncio +async def test_create_and_initialize_account( + program: Program, + initialized_account: Keypair, +) -> None: + """Test creating and initializing account in single tx.""" + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 1234 + + +@mark.asyncio +async def test_update_previously_created_account( + initialized_account: Keypair, + program: Program, +) -> None: + """Test updating a previously created account.""" + await program.rpc["update"]( + 4321, + ctx=Context(accounts={"my_account": initialized_account.pubkey()}), + ) + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 4321 + +``` + +You can just run these tests with `pytest` (or have your IDE run them). +!!! note + There is also a lower-level `localnet_fixture` function that sets up a localnet for a + particular project but doesn't return a workspace. + +## 2. Anchor test + + +Anchor lets you run whatever tests you want using the `[scripts]` section of `Anchor.toml`. +This means we can call Pytest inside the `anchor test` workflow. This is more limited +than the Pytest plugin but is more like the standard Anchor way of doing things. + +Here's how the `basic-1` tests look using `anchor test` and Pytest (but not the ): + +```python +# test_basic_1.py +import asyncio +from pathlib import Path +from pytest import fixture, mark +from anchorpy import create_workspace, close_workspace, Context, Program +from solders.keypair import Keypair +from solders.system_program import ID as SYS_PROGRAM_ID + + +# Since our other fixtures have module scope, we need to define +# this event_loop fixture and give it module scope otherwise +# pytest-asyncio will break. + +@fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for each test case.""" + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() + + +@fixture(scope="module") +async def program() -> Program: + workspace = create_workspace() + yield workspace["basic_1"] + await close_workspace(workspace) + + +@fixture(scope="module") +async def initialized_account(program: Program) -> Keypair: + my_account = Keypair() + await program.rpc["initialize"]( + 1234, + ctx=Context( + accounts={ + "my_account": my_account.pubkey(), + "user": program.provider.wallet.public_key, + "system_program": SYS_PROGRAM_ID, + }, + signers=[my_account], + ), + ) + return my_account + + +@mark.asyncio +async def test_create_and_initialize_account( + program: Program, initialized_account: Keypair +) -> None: + """Test creating and initializing account in single tx.""" + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 1234 + + +@mark.asyncio +async def test_update_previously_created_account( + initialized_account: Keypair, program: Program +) -> None: + """Test updating a previously created account.""" + await program.rpc["update"]( + 4321, ctx=Context(accounts={"myAccount": initialized_account.pubkey()}) + ) + account = await program.account["MyAccount"].fetch(initialized_account.pubkey()) + assert account.data == 4321 + +``` + +Just paste this code into a file called `test_basic_1.py` +in `anchor/examples/tutorial/basic-1/tests/`, and change the `scripts` section of `Anchor.toml` +to look like this: + +```toml +[scripts] +test = "pytest" + +``` + +Then run `anchor test` and voila!. + +!!! note + You must have `pytest-asyncio` installed for `test_basic_1.py` to work. + +## Bankrun Integration + +AnchorPy comes with a `bankrun_fixture` to help use +[`solders.bankrun`](https://kevinheavey.github.io/solders/examples/bankrun.html) in tests. + +If you haven't heard, `bankrun` is a much faster and more convenient alternative to +`solana-test-validator`. Long test suites are about 40 times faster with `bankrun`, +and for short test suites the difference is even bigger because `bankrun` has neglible +startup time. + +`bankrun_fixture` calls `bankrun.start()` and deploys all the programs in the current +Anchor workspace to the test envioronment. +Check out [this example](https://github.com/kevinheavey/anchorpy/blob/main/tests/test_basic_3_bankrun.py). + diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/__init__.py b/basics/anchorpy-main/examples/client-gen/basic_2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/accounts/__init__.py b/basics/anchorpy-main/examples/client-gen/basic_2/accounts/__init__.py new file mode 100644 index 0000000..4917931 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/accounts/__init__.py @@ -0,0 +1 @@ +from .counter import Counter, CounterJSON diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/accounts/counter.py b/basics/anchorpy-main/examples/client-gen/basic_2/accounts/counter.py new file mode 100644 index 0000000..c6712f4 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/accounts/counter.py @@ -0,0 +1,87 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey +from ..program_id import PROGRAM_ID + + +class CounterJSON(typing.TypedDict): + authority: str + count: int + + +@dataclass +class Counter: + discriminator: typing.ClassVar = b"\xff\xb0\x04\xf5\xbc\xfd|\x19" + layout: typing.ClassVar = borsh.CStruct( + "authority" / BorshPubkey, "count" / borsh.U64 + ) + authority: Pubkey + count: int + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["Counter"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["Counter"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["Counter"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "Counter": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = Counter.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + authority=dec.authority, + count=dec.count, + ) + + def to_json(self) -> CounterJSON: + return { + "authority": str(self.authority), + "count": self.count, + } + + @classmethod + def from_json(cls, obj: CounterJSON) -> "Counter": + return cls( + authority=Pubkey.from_string(obj["authority"]), + count=obj["count"], + ) diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/errors/__init__.py b/basics/anchorpy-main/examples/client-gen/basic_2/errors/__init__.py new file mode 100644 index 0000000..b453d3f --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/errors/__init__.py @@ -0,0 +1,26 @@ +import typing +import re +from solders.transaction_status import ( + InstructionErrorCustom, + TransactionErrorInstructionError, +) +from solana.rpc.core import RPCException +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from anchorpy.error import extract_code_and_logs +from ..program_id import PROGRAM_ID +from . import anchor + + +def from_code(code: int) -> typing.Optional[anchor.AnchorError]: + return anchor.from_code(code) + + +error_re = re.compile(r"Program (\w+) failed: custom program error: (\w+)") + + +def from_tx_error(error: RPCException) -> typing.Optional[anchor.AnchorError]: + err_info = error.args[0] + extracted = extract_code_and_logs(err_info, PROGRAM_ID) + if extracted is None: + return None + return from_code(extracted[0]) diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/errors/anchor.py b/basics/anchorpy-main/examples/client-gen/basic_2/errors/anchor.py new file mode 100644 index 0000000..3f266ef --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/errors/anchor.py @@ -0,0 +1,590 @@ +import typing +from anchorpy.error import ProgramError + + +class InstructionMissing(ProgramError): + def __init__(self): + super().__init__(100, "8 byte instruction identifier not provided") + + code = 100 + name = "InstructionMissing" + msg = "8 byte instruction identifier not provided" + + +class InstructionFallbackNotFound(ProgramError): + def __init__(self): + super().__init__(101, "Fallback functions are not supported") + + code = 101 + name = "InstructionFallbackNotFound" + msg = "Fallback functions are not supported" + + +class InstructionDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(102, "The program could not deserialize the given instruction") + + code = 102 + name = "InstructionDidNotDeserialize" + msg = "The program could not deserialize the given instruction" + + +class InstructionDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(103, "The program could not serialize the given instruction") + + code = 103 + name = "InstructionDidNotSerialize" + msg = "The program could not serialize the given instruction" + + +class IdlInstructionStub(ProgramError): + def __init__(self): + super().__init__(1000, "The program was compiled without idl instructions") + + code = 1000 + name = "IdlInstructionStub" + msg = "The program was compiled without idl instructions" + + +class IdlInstructionInvalidProgram(ProgramError): + def __init__(self): + super().__init__( + 1001, "The transaction was given an invalid program for the IDL instruction" + ) + + code = 1001 + name = "IdlInstructionInvalidProgram" + msg = "The transaction was given an invalid program for the IDL instruction" + + +class ConstraintMut(ProgramError): + def __init__(self): + super().__init__(2000, "A mut constraint was violated") + + code = 2000 + name = "ConstraintMut" + msg = "A mut constraint was violated" + + +class ConstraintHasOne(ProgramError): + def __init__(self): + super().__init__(2001, "A has_one constraint was violated") + + code = 2001 + name = "ConstraintHasOne" + msg = "A has_one constraint was violated" + + +class ConstraintSigner(ProgramError): + def __init__(self): + super().__init__(2002, "A signer constraint was violated") + + code = 2002 + name = "ConstraintSigner" + msg = "A signer constraint was violated" + + +class ConstraintRaw(ProgramError): + def __init__(self): + super().__init__(2003, "A raw constraint was violated") + + code = 2003 + name = "ConstraintRaw" + msg = "A raw constraint was violated" + + +class ConstraintOwner(ProgramError): + def __init__(self): + super().__init__(2004, "An owner constraint was violated") + + code = 2004 + name = "ConstraintOwner" + msg = "An owner constraint was violated" + + +class ConstraintRentExempt(ProgramError): + def __init__(self): + super().__init__(2005, "A rent exempt constraint was violated") + + code = 2005 + name = "ConstraintRentExempt" + msg = "A rent exempt constraint was violated" + + +class ConstraintSeeds(ProgramError): + def __init__(self): + super().__init__(2006, "A seeds constraint was violated") + + code = 2006 + name = "ConstraintSeeds" + msg = "A seeds constraint was violated" + + +class ConstraintExecutable(ProgramError): + def __init__(self): + super().__init__(2007, "An executable constraint was violated") + + code = 2007 + name = "ConstraintExecutable" + msg = "An executable constraint was violated" + + +class ConstraintState(ProgramError): + def __init__(self): + super().__init__(2008, "A state constraint was violated") + + code = 2008 + name = "ConstraintState" + msg = "A state constraint was violated" + + +class ConstraintAssociated(ProgramError): + def __init__(self): + super().__init__(2009, "An associated constraint was violated") + + code = 2009 + name = "ConstraintAssociated" + msg = "An associated constraint was violated" + + +class ConstraintAssociatedInit(ProgramError): + def __init__(self): + super().__init__(2010, "An associated init constraint was violated") + + code = 2010 + name = "ConstraintAssociatedInit" + msg = "An associated init constraint was violated" + + +class ConstraintClose(ProgramError): + def __init__(self): + super().__init__(2011, "A close constraint was violated") + + code = 2011 + name = "ConstraintClose" + msg = "A close constraint was violated" + + +class ConstraintAddress(ProgramError): + def __init__(self): + super().__init__(2012, "An address constraint was violated") + + code = 2012 + name = "ConstraintAddress" + msg = "An address constraint was violated" + + +class ConstraintZero(ProgramError): + def __init__(self): + super().__init__(2013, "Expected zero account discriminant") + + code = 2013 + name = "ConstraintZero" + msg = "Expected zero account discriminant" + + +class ConstraintTokenMint(ProgramError): + def __init__(self): + super().__init__(2014, "A token mint constraint was violated") + + code = 2014 + name = "ConstraintTokenMint" + msg = "A token mint constraint was violated" + + +class ConstraintTokenOwner(ProgramError): + def __init__(self): + super().__init__(2015, "A token owner constraint was violated") + + code = 2015 + name = "ConstraintTokenOwner" + msg = "A token owner constraint was violated" + + +class ConstraintMintMintAuthority(ProgramError): + def __init__(self): + super().__init__(2016, "A mint mint authority constraint was violated") + + code = 2016 + name = "ConstraintMintMintAuthority" + msg = "A mint mint authority constraint was violated" + + +class ConstraintMintFreezeAuthority(ProgramError): + def __init__(self): + super().__init__(2017, "A mint freeze authority constraint was violated") + + code = 2017 + name = "ConstraintMintFreezeAuthority" + msg = "A mint freeze authority constraint was violated" + + +class ConstraintMintDecimals(ProgramError): + def __init__(self): + super().__init__(2018, "A mint decimals constraint was violated") + + code = 2018 + name = "ConstraintMintDecimals" + msg = "A mint decimals constraint was violated" + + +class ConstraintSpace(ProgramError): + def __init__(self): + super().__init__(2019, "A space constraint was violated") + + code = 2019 + name = "ConstraintSpace" + msg = "A space constraint was violated" + + +class RequireViolated(ProgramError): + def __init__(self): + super().__init__(2500, "A require expression was violated") + + code = 2500 + name = "RequireViolated" + msg = "A require expression was violated" + + +class RequireEqViolated(ProgramError): + def __init__(self): + super().__init__(2501, "A require_eq expression was violated") + + code = 2501 + name = "RequireEqViolated" + msg = "A require_eq expression was violated" + + +class RequireKeysEqViolated(ProgramError): + def __init__(self): + super().__init__(2502, "A require_keys_eq expression was violated") + + code = 2502 + name = "RequireKeysEqViolated" + msg = "A require_keys_eq expression was violated" + + +class RequireNeqViolated(ProgramError): + def __init__(self): + super().__init__(2503, "A require_neq expression was violated") + + code = 2503 + name = "RequireNeqViolated" + msg = "A require_neq expression was violated" + + +class RequireKeysNeqViolated(ProgramError): + def __init__(self): + super().__init__(2504, "A require_keys_neq expression was violated") + + code = 2504 + name = "RequireKeysNeqViolated" + msg = "A require_keys_neq expression was violated" + + +class RequireGtViolated(ProgramError): + def __init__(self): + super().__init__(2505, "A require_gt expression was violated") + + code = 2505 + name = "RequireGtViolated" + msg = "A require_gt expression was violated" + + +class RequireGteViolated(ProgramError): + def __init__(self): + super().__init__(2506, "A require_gte expression was violated") + + code = 2506 + name = "RequireGteViolated" + msg = "A require_gte expression was violated" + + +class AccountDiscriminatorAlreadySet(ProgramError): + def __init__(self): + super().__init__( + 3000, "The account discriminator was already set on this account" + ) + + code = 3000 + name = "AccountDiscriminatorAlreadySet" + msg = "The account discriminator was already set on this account" + + +class AccountDiscriminatorNotFound(ProgramError): + def __init__(self): + super().__init__(3001, "No 8 byte discriminator was found on the account") + + code = 3001 + name = "AccountDiscriminatorNotFound" + msg = "No 8 byte discriminator was found on the account" + + +class AccountDiscriminatorMismatch(ProgramError): + def __init__(self): + super().__init__(3002, "8 byte discriminator did not match what was expected") + + code = 3002 + name = "AccountDiscriminatorMismatch" + msg = "8 byte discriminator did not match what was expected" + + +class AccountDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(3003, "Failed to deserialize the account") + + code = 3003 + name = "AccountDidNotDeserialize" + msg = "Failed to deserialize the account" + + +class AccountDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(3004, "Failed to serialize the account") + + code = 3004 + name = "AccountDidNotSerialize" + msg = "Failed to serialize the account" + + +class AccountNotEnoughKeys(ProgramError): + def __init__(self): + super().__init__(3005, "Not enough account keys given to the instruction") + + code = 3005 + name = "AccountNotEnoughKeys" + msg = "Not enough account keys given to the instruction" + + +class AccountNotMutable(ProgramError): + def __init__(self): + super().__init__(3006, "The given account is not mutable") + + code = 3006 + name = "AccountNotMutable" + msg = "The given account is not mutable" + + +class AccountOwnedByWrongProgram(ProgramError): + def __init__(self): + super().__init__( + 3007, "The given account is owned by a different program than expected" + ) + + code = 3007 + name = "AccountOwnedByWrongProgram" + msg = "The given account is owned by a different program than expected" + + +class InvalidProgramId(ProgramError): + def __init__(self): + super().__init__(3008, "Program ID was not as expected") + + code = 3008 + name = "InvalidProgramId" + msg = "Program ID was not as expected" + + +class InvalidProgramExecutable(ProgramError): + def __init__(self): + super().__init__(3009, "Program account is not executable") + + code = 3009 + name = "InvalidProgramExecutable" + msg = "Program account is not executable" + + +class AccountNotSigner(ProgramError): + def __init__(self): + super().__init__(3010, "The given account did not sign") + + code = 3010 + name = "AccountNotSigner" + msg = "The given account did not sign" + + +class AccountNotSystemOwned(ProgramError): + def __init__(self): + super().__init__(3011, "The given account is not owned by the system program") + + code = 3011 + name = "AccountNotSystemOwned" + msg = "The given account is not owned by the system program" + + +class AccountNotInitialized(ProgramError): + def __init__(self): + super().__init__( + 3012, "The program expected this account to be already initialized" + ) + + code = 3012 + name = "AccountNotInitialized" + msg = "The program expected this account to be already initialized" + + +class AccountNotProgramData(ProgramError): + def __init__(self): + super().__init__(3013, "The given account is not a program data account") + + code = 3013 + name = "AccountNotProgramData" + msg = "The given account is not a program data account" + + +class AccountNotAssociatedTokenAccount(ProgramError): + def __init__(self): + super().__init__(3014, "The given account is not the associated token account") + + code = 3014 + name = "AccountNotAssociatedTokenAccount" + msg = "The given account is not the associated token account" + + +class AccountSysvarMismatch(ProgramError): + def __init__(self): + super().__init__( + 3015, "The given public key does not match the required sysvar" + ) + + code = 3015 + name = "AccountSysvarMismatch" + msg = "The given public key does not match the required sysvar" + + +class StateInvalidAddress(ProgramError): + def __init__(self): + super().__init__( + 4000, "The given state account does not have the correct address" + ) + + code = 4000 + name = "StateInvalidAddress" + msg = "The given state account does not have the correct address" + + +class Deprecated(ProgramError): + def __init__(self): + super().__init__( + 5000, "The API being used is deprecated and should no longer be used" + ) + + code = 5000 + name = "Deprecated" + msg = "The API being used is deprecated and should no longer be used" + + +AnchorError = typing.Union[ + InstructionMissing, + InstructionFallbackNotFound, + InstructionDidNotDeserialize, + InstructionDidNotSerialize, + IdlInstructionStub, + IdlInstructionInvalidProgram, + ConstraintMut, + ConstraintHasOne, + ConstraintSigner, + ConstraintRaw, + ConstraintOwner, + ConstraintRentExempt, + ConstraintSeeds, + ConstraintExecutable, + ConstraintState, + ConstraintAssociated, + ConstraintAssociatedInit, + ConstraintClose, + ConstraintAddress, + ConstraintZero, + ConstraintTokenMint, + ConstraintTokenOwner, + ConstraintMintMintAuthority, + ConstraintMintFreezeAuthority, + ConstraintMintDecimals, + ConstraintSpace, + RequireViolated, + RequireEqViolated, + RequireKeysEqViolated, + RequireNeqViolated, + RequireKeysNeqViolated, + RequireGtViolated, + RequireGteViolated, + AccountDiscriminatorAlreadySet, + AccountDiscriminatorNotFound, + AccountDiscriminatorMismatch, + AccountDidNotDeserialize, + AccountDidNotSerialize, + AccountNotEnoughKeys, + AccountNotMutable, + AccountOwnedByWrongProgram, + InvalidProgramId, + InvalidProgramExecutable, + AccountNotSigner, + AccountNotSystemOwned, + AccountNotInitialized, + AccountNotProgramData, + AccountNotAssociatedTokenAccount, + AccountSysvarMismatch, + StateInvalidAddress, + Deprecated, +] +ANCHOR_ERROR_MAP: dict[int, AnchorError] = { + 100: InstructionMissing(), + 101: InstructionFallbackNotFound(), + 102: InstructionDidNotDeserialize(), + 103: InstructionDidNotSerialize(), + 1000: IdlInstructionStub(), + 1001: IdlInstructionInvalidProgram(), + 2000: ConstraintMut(), + 2001: ConstraintHasOne(), + 2002: ConstraintSigner(), + 2003: ConstraintRaw(), + 2004: ConstraintOwner(), + 2005: ConstraintRentExempt(), + 2006: ConstraintSeeds(), + 2007: ConstraintExecutable(), + 2008: ConstraintState(), + 2009: ConstraintAssociated(), + 2010: ConstraintAssociatedInit(), + 2011: ConstraintClose(), + 2012: ConstraintAddress(), + 2013: ConstraintZero(), + 2014: ConstraintTokenMint(), + 2015: ConstraintTokenOwner(), + 2016: ConstraintMintMintAuthority(), + 2017: ConstraintMintFreezeAuthority(), + 2018: ConstraintMintDecimals(), + 2019: ConstraintSpace(), + 2500: RequireViolated(), + 2501: RequireEqViolated(), + 2502: RequireKeysEqViolated(), + 2503: RequireNeqViolated(), + 2504: RequireKeysNeqViolated(), + 2505: RequireGtViolated(), + 2506: RequireGteViolated(), + 3000: AccountDiscriminatorAlreadySet(), + 3001: AccountDiscriminatorNotFound(), + 3002: AccountDiscriminatorMismatch(), + 3003: AccountDidNotDeserialize(), + 3004: AccountDidNotSerialize(), + 3005: AccountNotEnoughKeys(), + 3006: AccountNotMutable(), + 3007: AccountOwnedByWrongProgram(), + 3008: InvalidProgramId(), + 3009: InvalidProgramExecutable(), + 3010: AccountNotSigner(), + 3011: AccountNotSystemOwned(), + 3012: AccountNotInitialized(), + 3013: AccountNotProgramData(), + 3014: AccountNotAssociatedTokenAccount(), + 3015: AccountSysvarMismatch(), + 4000: StateInvalidAddress(), + 5000: Deprecated(), +} + + +def from_code(code: int) -> typing.Optional[AnchorError]: + maybe_err = ANCHOR_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/instructions/__init__.py b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/__init__.py new file mode 100644 index 0000000..5d579c6 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/__init__.py @@ -0,0 +1,2 @@ +from .create import create, CreateArgs, CreateAccounts +from .increment import increment, IncrementAccounts diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/instructions/create.py b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/create.py new file mode 100644 index 0000000..ed2b3fc --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/create.py @@ -0,0 +1,41 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.sysvar import RENT +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class CreateArgs(typing.TypedDict): + authority: Pubkey + + +layout = borsh.CStruct("authority" / BorshPubkey) + + +class CreateAccounts(typing.TypedDict): + counter: Pubkey + + +def create( + args: CreateArgs, + accounts: CreateAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["counter"], is_signer=False, is_writable=True), + AccountMeta(pubkey=RENT, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x18\x1e\xc8(\x05\x1c\x07w" + encoded_args = layout.build( + { + "authority": args["authority"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/instructions/increment.py b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/increment.py new file mode 100644 index 0000000..4c5ea80 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/instructions/increment.py @@ -0,0 +1,27 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +from ..program_id import PROGRAM_ID + + +class IncrementAccounts(typing.TypedDict): + counter: Pubkey + authority: Pubkey + + +def increment( + accounts: IncrementAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["counter"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["authority"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\x0b\x12h\th\xae;!" + encoded_args = b"" + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/examples/client-gen/basic_2/program_id.py b/basics/anchorpy-main/examples/client-gen/basic_2/program_id.py new file mode 100644 index 0000000..649b93e --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/basic_2/program_id.py @@ -0,0 +1,3 @@ +from solders.pubkey import Pubkey + +PROGRAM_ID = Pubkey.from_string("3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8") diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/__init__.py b/basics/anchorpy-main/examples/client-gen/tictactoe/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/__init__.py b/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/__init__.py new file mode 100644 index 0000000..fb255e9 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/__init__.py @@ -0,0 +1 @@ +from .game import Game, GameJSON diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/game.py b/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/game.py new file mode 100644 index 0000000..3c26fb9 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/accounts/game.py @@ -0,0 +1,135 @@ +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +import borsh_construct as borsh +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.error import AccountInvalidDiscriminator +from anchorpy.utils.rpc import get_multiple_accounts +from anchorpy.borsh_extension import BorshPubkey +from ..program_id import PROGRAM_ID +from .. import types + + +class GameJSON(typing.TypedDict): + players: list[str] + turn: int + board: list[list[typing.Optional[types.sign.SignJSON]]] + state: types.game_state.GameStateJSON + + +@dataclass +class Game: + discriminator: typing.ClassVar = b"\x1bZ\xa6}Jdy\x12" + layout: typing.ClassVar = borsh.CStruct( + "players" / BorshPubkey[2], + "turn" / borsh.U8, + "board" / borsh.Option(types.sign.layout)[3][3], + "state" / types.game_state.layout, + ) + players: list[Pubkey] + turn: int + board: list[list[typing.Optional[types.sign.SignKind]]] + state: types.game_state.GameStateKind + + @classmethod + async def fetch( + cls, + conn: AsyncClient, + address: Pubkey, + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.Optional["Game"]: + resp = await conn.get_account_info(address, commitment=commitment) + info = resp.value + if info is None: + return None + if info.owner != program_id: + raise ValueError("Account does not belong to this program") + bytes_data = info.data + return cls.decode(bytes_data) + + @classmethod + async def fetch_multiple( + cls, + conn: AsyncClient, + addresses: list[Pubkey], + commitment: typing.Optional[Commitment] = None, + program_id: Pubkey = PROGRAM_ID, + ) -> typing.List[typing.Optional["Game"]]: + infos = await get_multiple_accounts(conn, addresses, commitment=commitment) + res: typing.List[typing.Optional["Game"]] = [] + for info in infos: + if info is None: + res.append(None) + continue + if info.account.owner != program_id: + raise ValueError("Account does not belong to this program") + res.append(cls.decode(info.account.data)) + return res + + @classmethod + def decode(cls, data: bytes) -> "Game": + if data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator: + raise AccountInvalidDiscriminator( + "The discriminator for this account is invalid" + ) + dec = Game.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:]) + return cls( + players=dec.players, + turn=dec.turn, + board=list( + map( + lambda item: list( + map( + lambda item: ( + None if item is None else types.sign.from_decoded(item) + ), + item, + ) + ), + dec.board, + ) + ), + state=types.game_state.from_decoded(dec.state), + ) + + def to_json(self) -> GameJSON: + return { + "players": list(map(lambda item: str(item), self.players)), + "turn": self.turn, + "board": list( + map( + lambda item: list( + map( + lambda item: (None if item is None else item.to_json()), + item, + ) + ), + self.board, + ) + ), + "state": self.state.to_json(), + } + + @classmethod + def from_json(cls, obj: GameJSON) -> "Game": + return cls( + players=list(map(lambda item: Pubkey.from_string(item), obj["players"])), + turn=obj["turn"], + board=list( + map( + lambda item: list( + map( + lambda item: ( + None if item is None else types.sign.from_json(item) + ), + item, + ) + ), + obj["board"], + ) + ), + state=types.game_state.from_json(obj["state"]), + ) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/errors/__init__.py b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/__init__.py new file mode 100644 index 0000000..421993d --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/__init__.py @@ -0,0 +1,29 @@ +import typing +import re +from solders.transaction_status import ( + InstructionErrorCustom, + TransactionErrorInstructionError, +) +from solana.rpc.core import RPCException +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from anchorpy.error import extract_code_and_logs +from ..program_id import PROGRAM_ID +from . import anchor +from . import custom + + +def from_code(code: int) -> typing.Union[custom.CustomError, anchor.AnchorError, None]: + return custom.from_code(code) if code >= 6000 else anchor.from_code(code) + + +error_re = re.compile(r"Program (\w+) failed: custom program error: (\w+)") + + +def from_tx_error( + error: RPCException, +) -> typing.Union[anchor.AnchorError, custom.CustomError, None]: + err_info = error.args[0] + extracted = extract_code_and_logs(err_info, PROGRAM_ID) + if extracted is None: + return None + return from_code(extracted[0]) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/errors/anchor.py b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/anchor.py new file mode 100644 index 0000000..3f266ef --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/anchor.py @@ -0,0 +1,590 @@ +import typing +from anchorpy.error import ProgramError + + +class InstructionMissing(ProgramError): + def __init__(self): + super().__init__(100, "8 byte instruction identifier not provided") + + code = 100 + name = "InstructionMissing" + msg = "8 byte instruction identifier not provided" + + +class InstructionFallbackNotFound(ProgramError): + def __init__(self): + super().__init__(101, "Fallback functions are not supported") + + code = 101 + name = "InstructionFallbackNotFound" + msg = "Fallback functions are not supported" + + +class InstructionDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(102, "The program could not deserialize the given instruction") + + code = 102 + name = "InstructionDidNotDeserialize" + msg = "The program could not deserialize the given instruction" + + +class InstructionDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(103, "The program could not serialize the given instruction") + + code = 103 + name = "InstructionDidNotSerialize" + msg = "The program could not serialize the given instruction" + + +class IdlInstructionStub(ProgramError): + def __init__(self): + super().__init__(1000, "The program was compiled without idl instructions") + + code = 1000 + name = "IdlInstructionStub" + msg = "The program was compiled without idl instructions" + + +class IdlInstructionInvalidProgram(ProgramError): + def __init__(self): + super().__init__( + 1001, "The transaction was given an invalid program for the IDL instruction" + ) + + code = 1001 + name = "IdlInstructionInvalidProgram" + msg = "The transaction was given an invalid program for the IDL instruction" + + +class ConstraintMut(ProgramError): + def __init__(self): + super().__init__(2000, "A mut constraint was violated") + + code = 2000 + name = "ConstraintMut" + msg = "A mut constraint was violated" + + +class ConstraintHasOne(ProgramError): + def __init__(self): + super().__init__(2001, "A has_one constraint was violated") + + code = 2001 + name = "ConstraintHasOne" + msg = "A has_one constraint was violated" + + +class ConstraintSigner(ProgramError): + def __init__(self): + super().__init__(2002, "A signer constraint was violated") + + code = 2002 + name = "ConstraintSigner" + msg = "A signer constraint was violated" + + +class ConstraintRaw(ProgramError): + def __init__(self): + super().__init__(2003, "A raw constraint was violated") + + code = 2003 + name = "ConstraintRaw" + msg = "A raw constraint was violated" + + +class ConstraintOwner(ProgramError): + def __init__(self): + super().__init__(2004, "An owner constraint was violated") + + code = 2004 + name = "ConstraintOwner" + msg = "An owner constraint was violated" + + +class ConstraintRentExempt(ProgramError): + def __init__(self): + super().__init__(2005, "A rent exempt constraint was violated") + + code = 2005 + name = "ConstraintRentExempt" + msg = "A rent exempt constraint was violated" + + +class ConstraintSeeds(ProgramError): + def __init__(self): + super().__init__(2006, "A seeds constraint was violated") + + code = 2006 + name = "ConstraintSeeds" + msg = "A seeds constraint was violated" + + +class ConstraintExecutable(ProgramError): + def __init__(self): + super().__init__(2007, "An executable constraint was violated") + + code = 2007 + name = "ConstraintExecutable" + msg = "An executable constraint was violated" + + +class ConstraintState(ProgramError): + def __init__(self): + super().__init__(2008, "A state constraint was violated") + + code = 2008 + name = "ConstraintState" + msg = "A state constraint was violated" + + +class ConstraintAssociated(ProgramError): + def __init__(self): + super().__init__(2009, "An associated constraint was violated") + + code = 2009 + name = "ConstraintAssociated" + msg = "An associated constraint was violated" + + +class ConstraintAssociatedInit(ProgramError): + def __init__(self): + super().__init__(2010, "An associated init constraint was violated") + + code = 2010 + name = "ConstraintAssociatedInit" + msg = "An associated init constraint was violated" + + +class ConstraintClose(ProgramError): + def __init__(self): + super().__init__(2011, "A close constraint was violated") + + code = 2011 + name = "ConstraintClose" + msg = "A close constraint was violated" + + +class ConstraintAddress(ProgramError): + def __init__(self): + super().__init__(2012, "An address constraint was violated") + + code = 2012 + name = "ConstraintAddress" + msg = "An address constraint was violated" + + +class ConstraintZero(ProgramError): + def __init__(self): + super().__init__(2013, "Expected zero account discriminant") + + code = 2013 + name = "ConstraintZero" + msg = "Expected zero account discriminant" + + +class ConstraintTokenMint(ProgramError): + def __init__(self): + super().__init__(2014, "A token mint constraint was violated") + + code = 2014 + name = "ConstraintTokenMint" + msg = "A token mint constraint was violated" + + +class ConstraintTokenOwner(ProgramError): + def __init__(self): + super().__init__(2015, "A token owner constraint was violated") + + code = 2015 + name = "ConstraintTokenOwner" + msg = "A token owner constraint was violated" + + +class ConstraintMintMintAuthority(ProgramError): + def __init__(self): + super().__init__(2016, "A mint mint authority constraint was violated") + + code = 2016 + name = "ConstraintMintMintAuthority" + msg = "A mint mint authority constraint was violated" + + +class ConstraintMintFreezeAuthority(ProgramError): + def __init__(self): + super().__init__(2017, "A mint freeze authority constraint was violated") + + code = 2017 + name = "ConstraintMintFreezeAuthority" + msg = "A mint freeze authority constraint was violated" + + +class ConstraintMintDecimals(ProgramError): + def __init__(self): + super().__init__(2018, "A mint decimals constraint was violated") + + code = 2018 + name = "ConstraintMintDecimals" + msg = "A mint decimals constraint was violated" + + +class ConstraintSpace(ProgramError): + def __init__(self): + super().__init__(2019, "A space constraint was violated") + + code = 2019 + name = "ConstraintSpace" + msg = "A space constraint was violated" + + +class RequireViolated(ProgramError): + def __init__(self): + super().__init__(2500, "A require expression was violated") + + code = 2500 + name = "RequireViolated" + msg = "A require expression was violated" + + +class RequireEqViolated(ProgramError): + def __init__(self): + super().__init__(2501, "A require_eq expression was violated") + + code = 2501 + name = "RequireEqViolated" + msg = "A require_eq expression was violated" + + +class RequireKeysEqViolated(ProgramError): + def __init__(self): + super().__init__(2502, "A require_keys_eq expression was violated") + + code = 2502 + name = "RequireKeysEqViolated" + msg = "A require_keys_eq expression was violated" + + +class RequireNeqViolated(ProgramError): + def __init__(self): + super().__init__(2503, "A require_neq expression was violated") + + code = 2503 + name = "RequireNeqViolated" + msg = "A require_neq expression was violated" + + +class RequireKeysNeqViolated(ProgramError): + def __init__(self): + super().__init__(2504, "A require_keys_neq expression was violated") + + code = 2504 + name = "RequireKeysNeqViolated" + msg = "A require_keys_neq expression was violated" + + +class RequireGtViolated(ProgramError): + def __init__(self): + super().__init__(2505, "A require_gt expression was violated") + + code = 2505 + name = "RequireGtViolated" + msg = "A require_gt expression was violated" + + +class RequireGteViolated(ProgramError): + def __init__(self): + super().__init__(2506, "A require_gte expression was violated") + + code = 2506 + name = "RequireGteViolated" + msg = "A require_gte expression was violated" + + +class AccountDiscriminatorAlreadySet(ProgramError): + def __init__(self): + super().__init__( + 3000, "The account discriminator was already set on this account" + ) + + code = 3000 + name = "AccountDiscriminatorAlreadySet" + msg = "The account discriminator was already set on this account" + + +class AccountDiscriminatorNotFound(ProgramError): + def __init__(self): + super().__init__(3001, "No 8 byte discriminator was found on the account") + + code = 3001 + name = "AccountDiscriminatorNotFound" + msg = "No 8 byte discriminator was found on the account" + + +class AccountDiscriminatorMismatch(ProgramError): + def __init__(self): + super().__init__(3002, "8 byte discriminator did not match what was expected") + + code = 3002 + name = "AccountDiscriminatorMismatch" + msg = "8 byte discriminator did not match what was expected" + + +class AccountDidNotDeserialize(ProgramError): + def __init__(self): + super().__init__(3003, "Failed to deserialize the account") + + code = 3003 + name = "AccountDidNotDeserialize" + msg = "Failed to deserialize the account" + + +class AccountDidNotSerialize(ProgramError): + def __init__(self): + super().__init__(3004, "Failed to serialize the account") + + code = 3004 + name = "AccountDidNotSerialize" + msg = "Failed to serialize the account" + + +class AccountNotEnoughKeys(ProgramError): + def __init__(self): + super().__init__(3005, "Not enough account keys given to the instruction") + + code = 3005 + name = "AccountNotEnoughKeys" + msg = "Not enough account keys given to the instruction" + + +class AccountNotMutable(ProgramError): + def __init__(self): + super().__init__(3006, "The given account is not mutable") + + code = 3006 + name = "AccountNotMutable" + msg = "The given account is not mutable" + + +class AccountOwnedByWrongProgram(ProgramError): + def __init__(self): + super().__init__( + 3007, "The given account is owned by a different program than expected" + ) + + code = 3007 + name = "AccountOwnedByWrongProgram" + msg = "The given account is owned by a different program than expected" + + +class InvalidProgramId(ProgramError): + def __init__(self): + super().__init__(3008, "Program ID was not as expected") + + code = 3008 + name = "InvalidProgramId" + msg = "Program ID was not as expected" + + +class InvalidProgramExecutable(ProgramError): + def __init__(self): + super().__init__(3009, "Program account is not executable") + + code = 3009 + name = "InvalidProgramExecutable" + msg = "Program account is not executable" + + +class AccountNotSigner(ProgramError): + def __init__(self): + super().__init__(3010, "The given account did not sign") + + code = 3010 + name = "AccountNotSigner" + msg = "The given account did not sign" + + +class AccountNotSystemOwned(ProgramError): + def __init__(self): + super().__init__(3011, "The given account is not owned by the system program") + + code = 3011 + name = "AccountNotSystemOwned" + msg = "The given account is not owned by the system program" + + +class AccountNotInitialized(ProgramError): + def __init__(self): + super().__init__( + 3012, "The program expected this account to be already initialized" + ) + + code = 3012 + name = "AccountNotInitialized" + msg = "The program expected this account to be already initialized" + + +class AccountNotProgramData(ProgramError): + def __init__(self): + super().__init__(3013, "The given account is not a program data account") + + code = 3013 + name = "AccountNotProgramData" + msg = "The given account is not a program data account" + + +class AccountNotAssociatedTokenAccount(ProgramError): + def __init__(self): + super().__init__(3014, "The given account is not the associated token account") + + code = 3014 + name = "AccountNotAssociatedTokenAccount" + msg = "The given account is not the associated token account" + + +class AccountSysvarMismatch(ProgramError): + def __init__(self): + super().__init__( + 3015, "The given public key does not match the required sysvar" + ) + + code = 3015 + name = "AccountSysvarMismatch" + msg = "The given public key does not match the required sysvar" + + +class StateInvalidAddress(ProgramError): + def __init__(self): + super().__init__( + 4000, "The given state account does not have the correct address" + ) + + code = 4000 + name = "StateInvalidAddress" + msg = "The given state account does not have the correct address" + + +class Deprecated(ProgramError): + def __init__(self): + super().__init__( + 5000, "The API being used is deprecated and should no longer be used" + ) + + code = 5000 + name = "Deprecated" + msg = "The API being used is deprecated and should no longer be used" + + +AnchorError = typing.Union[ + InstructionMissing, + InstructionFallbackNotFound, + InstructionDidNotDeserialize, + InstructionDidNotSerialize, + IdlInstructionStub, + IdlInstructionInvalidProgram, + ConstraintMut, + ConstraintHasOne, + ConstraintSigner, + ConstraintRaw, + ConstraintOwner, + ConstraintRentExempt, + ConstraintSeeds, + ConstraintExecutable, + ConstraintState, + ConstraintAssociated, + ConstraintAssociatedInit, + ConstraintClose, + ConstraintAddress, + ConstraintZero, + ConstraintTokenMint, + ConstraintTokenOwner, + ConstraintMintMintAuthority, + ConstraintMintFreezeAuthority, + ConstraintMintDecimals, + ConstraintSpace, + RequireViolated, + RequireEqViolated, + RequireKeysEqViolated, + RequireNeqViolated, + RequireKeysNeqViolated, + RequireGtViolated, + RequireGteViolated, + AccountDiscriminatorAlreadySet, + AccountDiscriminatorNotFound, + AccountDiscriminatorMismatch, + AccountDidNotDeserialize, + AccountDidNotSerialize, + AccountNotEnoughKeys, + AccountNotMutable, + AccountOwnedByWrongProgram, + InvalidProgramId, + InvalidProgramExecutable, + AccountNotSigner, + AccountNotSystemOwned, + AccountNotInitialized, + AccountNotProgramData, + AccountNotAssociatedTokenAccount, + AccountSysvarMismatch, + StateInvalidAddress, + Deprecated, +] +ANCHOR_ERROR_MAP: dict[int, AnchorError] = { + 100: InstructionMissing(), + 101: InstructionFallbackNotFound(), + 102: InstructionDidNotDeserialize(), + 103: InstructionDidNotSerialize(), + 1000: IdlInstructionStub(), + 1001: IdlInstructionInvalidProgram(), + 2000: ConstraintMut(), + 2001: ConstraintHasOne(), + 2002: ConstraintSigner(), + 2003: ConstraintRaw(), + 2004: ConstraintOwner(), + 2005: ConstraintRentExempt(), + 2006: ConstraintSeeds(), + 2007: ConstraintExecutable(), + 2008: ConstraintState(), + 2009: ConstraintAssociated(), + 2010: ConstraintAssociatedInit(), + 2011: ConstraintClose(), + 2012: ConstraintAddress(), + 2013: ConstraintZero(), + 2014: ConstraintTokenMint(), + 2015: ConstraintTokenOwner(), + 2016: ConstraintMintMintAuthority(), + 2017: ConstraintMintFreezeAuthority(), + 2018: ConstraintMintDecimals(), + 2019: ConstraintSpace(), + 2500: RequireViolated(), + 2501: RequireEqViolated(), + 2502: RequireKeysEqViolated(), + 2503: RequireNeqViolated(), + 2504: RequireKeysNeqViolated(), + 2505: RequireGtViolated(), + 2506: RequireGteViolated(), + 3000: AccountDiscriminatorAlreadySet(), + 3001: AccountDiscriminatorNotFound(), + 3002: AccountDiscriminatorMismatch(), + 3003: AccountDidNotDeserialize(), + 3004: AccountDidNotSerialize(), + 3005: AccountNotEnoughKeys(), + 3006: AccountNotMutable(), + 3007: AccountOwnedByWrongProgram(), + 3008: InvalidProgramId(), + 3009: InvalidProgramExecutable(), + 3010: AccountNotSigner(), + 3011: AccountNotSystemOwned(), + 3012: AccountNotInitialized(), + 3013: AccountNotProgramData(), + 3014: AccountNotAssociatedTokenAccount(), + 3015: AccountSysvarMismatch(), + 4000: StateInvalidAddress(), + 5000: Deprecated(), +} + + +def from_code(code: int) -> typing.Optional[AnchorError]: + maybe_err = ANCHOR_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/errors/custom.py b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/custom.py new file mode 100644 index 0000000..ea1923a --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/errors/custom.py @@ -0,0 +1,66 @@ +import typing +from anchorpy.error import ProgramError + + +class TileOutOfBounds(ProgramError): + def __init__(self) -> None: + super().__init__(6000, None) + + code = 6000 + name = "TileOutOfBounds" + msg = None + + +class TileAlreadySet(ProgramError): + def __init__(self) -> None: + super().__init__(6001, None) + + code = 6001 + name = "TileAlreadySet" + msg = None + + +class GameAlreadyOver(ProgramError): + def __init__(self) -> None: + super().__init__(6002, None) + + code = 6002 + name = "GameAlreadyOver" + msg = None + + +class NotPlayersTurn(ProgramError): + def __init__(self) -> None: + super().__init__(6003, None) + + code = 6003 + name = "NotPlayersTurn" + msg = None + + +class GameAlreadyStarted(ProgramError): + def __init__(self) -> None: + super().__init__(6004, None) + + code = 6004 + name = "GameAlreadyStarted" + msg = None + + +CustomError = typing.Union[ + TileOutOfBounds, TileAlreadySet, GameAlreadyOver, NotPlayersTurn, GameAlreadyStarted +] +CUSTOM_ERROR_MAP: dict[int, CustomError] = { + 6000: TileOutOfBounds(), + 6001: TileAlreadySet(), + 6002: GameAlreadyOver(), + 6003: NotPlayersTurn(), + 6004: GameAlreadyStarted(), +} + + +def from_code(code: int) -> typing.Optional[CustomError]: + maybe_err = CUSTOM_ERROR_MAP.get(code) + if maybe_err is None: + return None + return maybe_err diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/__init__.py b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/__init__.py new file mode 100644 index 0000000..739e79e --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/__init__.py @@ -0,0 +1,2 @@ +from .setup_game import setup_game, SetupGameArgs, SetupGameAccounts +from .play import play, PlayArgs, PlayAccounts diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/play.py b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/play.py new file mode 100644 index 0000000..ebd4f90 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/play.py @@ -0,0 +1,41 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.instruction import Instruction, AccountMeta +import borsh_construct as borsh +from .. import types +from ..program_id import PROGRAM_ID + + +class PlayArgs(typing.TypedDict): + tile: types.tile.Tile + + +layout = borsh.CStruct("tile" / types.tile.Tile.layout) + + +class PlayAccounts(typing.TypedDict): + game: Pubkey + player: Pubkey + + +def play( + args: PlayArgs, + accounts: PlayAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["game"], is_signer=False, is_writable=True), + AccountMeta(pubkey=accounts["player"], is_signer=True, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xd5\x9d\xc1\x8e\xe48\xf8\x96" + encoded_args = layout.build( + { + "tile": args["tile"].to_encodable(), + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/setup_game.py b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/setup_game.py new file mode 100644 index 0000000..15aad50 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/instructions/setup_game.py @@ -0,0 +1,43 @@ +from __future__ import annotations +import typing +from solders.pubkey import Pubkey +from solders.system_program import ID as SYS_PROGRAM_ID +from solders.instruction import Instruction, AccountMeta +from anchorpy.borsh_extension import BorshPubkey +import borsh_construct as borsh +from ..program_id import PROGRAM_ID + + +class SetupGameArgs(typing.TypedDict): + player_two: Pubkey + + +layout = borsh.CStruct("player_two" / BorshPubkey) + + +class SetupGameAccounts(typing.TypedDict): + game: Pubkey + player_one: Pubkey + + +def setup_game( + args: SetupGameArgs, + accounts: SetupGameAccounts, + program_id: Pubkey = PROGRAM_ID, + remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None, +) -> Instruction: + keys: list[AccountMeta] = [ + AccountMeta(pubkey=accounts["game"], is_signer=True, is_writable=True), + AccountMeta(pubkey=accounts["player_one"], is_signer=True, is_writable=True), + AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False), + ] + if remaining_accounts is not None: + keys += remaining_accounts + identifier = b"\xb4\xda\x80K:\xde#R" + encoded_args = layout.build( + { + "player_two": args["player_two"], + } + ) + data = identifier + encoded_args + return Instruction(program_id, data, keys) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/program_id.py b/basics/anchorpy-main/examples/client-gen/tictactoe/program_id.py new file mode 100644 index 0000000..649b93e --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/program_id.py @@ -0,0 +1,3 @@ +from solders.pubkey import Pubkey + +PROGRAM_ID = Pubkey.from_string("3rTQ3R4B2PxZrAyx7EUefySPgZY8RhJf16cZajbmrzp8") diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/types/__init__.py b/basics/anchorpy-main/examples/client-gen/tictactoe/types/__init__.py new file mode 100644 index 0000000..ee1a6a2 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/types/__init__.py @@ -0,0 +1,7 @@ +import typing +from . import tile +from .tile import Tile, TileJSON +from . import game_state +from .game_state import GameStateKind, GameStateJSON +from . import sign +from .sign import SignKind, SignJSON diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/types/game_state.py b/basics/anchorpy-main/examples/client-gen/tictactoe/types/game_state.py new file mode 100644 index 0000000..09643ca --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/types/game_state.py @@ -0,0 +1,129 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from solders.pubkey import Pubkey +from anchorpy.borsh_extension import EnumForCodegen, BorshPubkey +import borsh_construct as borsh + + +class WonJSONValue(typing.TypedDict): + winner: str + + +class WonValue(typing.TypedDict): + winner: Pubkey + + +class ActiveJSON(typing.TypedDict): + kind: typing.Literal["Active"] + + +class TieJSON(typing.TypedDict): + kind: typing.Literal["Tie"] + + +class WonJSON(typing.TypedDict): + value: WonJSONValue + kind: typing.Literal["Won"] + + +@dataclass +class Active: + discriminator: typing.ClassVar = 0 + kind: typing.ClassVar = "Active" + + @classmethod + def to_json(cls) -> ActiveJSON: + return ActiveJSON( + kind="Active", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "Active": {}, + } + + +@dataclass +class Tie: + discriminator: typing.ClassVar = 1 + kind: typing.ClassVar = "Tie" + + @classmethod + def to_json(cls) -> TieJSON: + return TieJSON( + kind="Tie", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "Tie": {}, + } + + +@dataclass +class Won: + discriminator: typing.ClassVar = 2 + kind: typing.ClassVar = "Won" + value: WonValue + + def to_json(self) -> WonJSON: + return WonJSON( + kind="Won", + value={ + "winner": str(self.value["winner"]), + }, + ) + + def to_encodable(self) -> dict: + return { + "Won": { + "winner": self.value["winner"], + }, + } + + +GameStateKind = typing.Union[Active, Tie, Won] +GameStateJSON = typing.Union[ActiveJSON, TieJSON, WonJSON] + + +def from_decoded(obj: dict) -> GameStateKind: + if not isinstance(obj, dict): + raise ValueError("Invalid enum object") + if "Active" in obj: + return Active() + if "Tie" in obj: + return Tie() + if "Won" in obj: + val = obj["Won"] + return Won( + WonValue( + winner=val["winner"], + ) + ) + raise ValueError("Invalid enum object") + + +def from_json(obj: GameStateJSON) -> GameStateKind: + if obj["kind"] == "Active": + return Active() + if obj["kind"] == "Tie": + return Tie() + if obj["kind"] == "Won": + won_json_value = typing.cast(WonJSONValue, obj["value"]) + return Won( + WonValue( + winner=Pubkey.from_string(won_json_value["winner"]), + ) + ) + kind = obj["kind"] + raise ValueError(f"Unrecognized enum kind: {kind}") + + +layout = EnumForCodegen( + "Active" / borsh.CStruct(), + "Tie" / borsh.CStruct(), + "Won" / borsh.CStruct("winner" / BorshPubkey), +) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/types/sign.py b/basics/anchorpy-main/examples/client-gen/tictactoe/types/sign.py new file mode 100644 index 0000000..0577e31 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/types/sign.py @@ -0,0 +1,75 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from anchorpy.borsh_extension import EnumForCodegen +import borsh_construct as borsh + + +class XJSON(typing.TypedDict): + kind: typing.Literal["X"] + + +class OJSON(typing.TypedDict): + kind: typing.Literal["O"] + + +@dataclass +class X: + discriminator: typing.ClassVar = 0 + kind: typing.ClassVar = "X" + + @classmethod + def to_json(cls) -> XJSON: + return XJSON( + kind="X", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "X": {}, + } + + +@dataclass +class O: + discriminator: typing.ClassVar = 1 + kind: typing.ClassVar = "O" + + @classmethod + def to_json(cls) -> OJSON: + return OJSON( + kind="O", + ) + + @classmethod + def to_encodable(cls) -> dict: + return { + "O": {}, + } + + +SignKind = typing.Union[X, O] +SignJSON = typing.Union[XJSON, OJSON] + + +def from_decoded(obj: dict) -> SignKind: + if not isinstance(obj, dict): + raise ValueError("Invalid enum object") + if "X" in obj: + return X() + if "O" in obj: + return O() + raise ValueError("Invalid enum object") + + +def from_json(obj: SignJSON) -> SignKind: + if obj["kind"] == "X": + return X() + if obj["kind"] == "O": + return O() + kind = obj["kind"] + raise ValueError(f"Unrecognized enum kind: {kind}") + + +layout = EnumForCodegen("X" / borsh.CStruct(), "O" / borsh.CStruct()) diff --git a/basics/anchorpy-main/examples/client-gen/tictactoe/types/tile.py b/basics/anchorpy-main/examples/client-gen/tictactoe/types/tile.py new file mode 100644 index 0000000..dcd9142 --- /dev/null +++ b/basics/anchorpy-main/examples/client-gen/tictactoe/types/tile.py @@ -0,0 +1,31 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass +from construct import Container +import borsh_construct as borsh + + +class TileJSON(typing.TypedDict): + row: int + column: int + + +@dataclass +class Tile: + layout: typing.ClassVar = borsh.CStruct("row" / borsh.U8, "column" / borsh.U8) + row: int + column: int + + @classmethod + def from_decoded(cls, obj: Container) -> "Tile": + return cls(row=obj.row, column=obj.column) + + def to_encodable(self) -> dict[str, typing.Any]: + return {"row": self.row, "column": self.column} + + def to_json(self) -> TileJSON: + return {"row": self.row, "column": self.column} + + @classmethod + def from_json(cls, obj: TileJSON) -> "Tile": + return cls(row=obj["row"], column=obj["column"]) diff --git a/basics/anchorpy-main/overrides/main.html b/basics/anchorpy-main/overrides/main.html new file mode 100644 index 0000000..ad5b6ad --- /dev/null +++ b/basics/anchorpy-main/overrides/main.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block extrahead %} + +{% endblock %} \ No newline at end of file diff --git a/basics/anchorpy-main/src/anchorpy/__init__.py b/basics/anchorpy-main/src/anchorpy/__init__.py new file mode 100644 index 0000000..9f4a059 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/__init__.py @@ -0,0 +1,52 @@ +"""The Python Anchor client.""" +from anchorpy_core.idl import Idl + +from anchorpy import error, utils +from anchorpy.coder.coder import AccountsCoder, Coder, EventCoder, InstructionCoder +from anchorpy.idl import IdlProgramAccount +from anchorpy.program.common import ( + Event, + NamedInstruction, + translate_address, + validate_accounts, +) +from anchorpy.program.context import Context +from anchorpy.program.core import Program +from anchorpy.program.event import EventParser +from anchorpy.program.namespace.account import AccountClient, ProgramAccount +from anchorpy.program.namespace.simulate import SimulateResponse +from anchorpy.provider import Provider, Wallet +from anchorpy.pytest_plugin import bankrun_fixture, localnet_fixture, workspace_fixture +from anchorpy.workspace import WorkspaceType, close_workspace, create_workspace + +__all__ = [ + "Program", + "Provider", + "Context", + "bankrun_fixture", + "create_workspace", + "close_workspace", + "Idl", + "workspace_fixture", + "WorkspaceType", + "localnet_fixture", + "Wallet", + "Coder", + "InstructionCoder", + "EventCoder", + "AccountsCoder", + "NamedInstruction", + "IdlProgramAccount", + "Event", + "translate_address", + "validate_accounts", + "AccountClient", + "ProgramAccount", + "EventParser", + "SimulateResponse", + "error", + "utils", +] + + +__version__ = "0.18.0" diff --git a/basics/anchorpy-main/src/anchorpy/borsh_extension.py b/basics/anchorpy-main/src/anchorpy/borsh_extension.py new file mode 100644 index 0000000..bd7f058 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/borsh_extension.py @@ -0,0 +1,121 @@ +"""Extensions to the Borsh spec for Solana-specific types.""" +from dataclasses import asdict +from keyword import kwlist +from typing import Any, Dict, Type, TypeVar, cast + +from borsh_construct import U8, CStruct +from construct import ( + Adapter, + Bytes, + Construct, + Container, + IfThenElse, + Padding, + Renamed, + Switch, +) +from solders import pubkey + + +class BorshPubkeyAdapter(Adapter): + def __init__(self) -> None: + super().__init__(Bytes(32)) # type: ignore + + def _decode(self, obj: bytes, context, path) -> pubkey.Pubkey: + return pubkey.Pubkey(obj) + + def _encode(self, obj: pubkey.Pubkey, context, path) -> bytes: + return bytes(obj) + + +class EnumForCodegen(Adapter): + _index_key = "index" + _value_key = "value" + + def __init__(self, *variants: "Renamed[CStruct, CStruct]") -> None: + """Init enum.""" + switch_cases: dict[int, "Renamed[CStruct, CStruct]"] = {} + variant_name_to_index: dict[str, int] = {} + index_to_variant_name: dict[int, str] = {} + for idx, parser in enumerate(variants): + switch_cases[idx] = parser + name = cast(str, parser.name) + variant_name_to_index[name] = idx + index_to_variant_name[idx] = name + enum_struct = CStruct( + self._index_key / U8, + self._value_key + / Switch(lambda this: this.index, cast(dict[int, Construct], switch_cases)), + ) + super().__init__(enum_struct) # type: ignore + self.variant_name_to_index = variant_name_to_index + self.index_to_variant_name = index_to_variant_name + + def _decode(self, obj: CStruct, context, path) -> dict[str, Any]: + index = obj.index + variant_name = self.index_to_variant_name[index] + return {variant_name: obj.value} + + def _encode(self, obj: dict[str, Any], context, path) -> dict[str, Any]: + variant_name = list(obj.keys())[0] + index = self.variant_name_to_index[variant_name] + return {self._index_key: index, self._value_key: obj[variant_name]} + + +class COption(Adapter): + _discriminator_key = "discriminator" + _value_key = "value" + + def __init__(self, subcon: Construct) -> None: + option_struct = CStruct( + self._discriminator_key / U8, + self._value_key + / IfThenElse( + lambda this: this[self._discriminator_key] == 0, + Padding(subcon.sizeof()), + subcon, + ), + ) + super().__init__(option_struct) # type: ignore + + def _decode(self, obj, context, path) -> Any: + discriminator = obj[self._discriminator_key] + return None if discriminator == 0 else obj[self._value_key] + + def _encode(self, obj, context, path) -> dict: + discriminator = 0 if obj is None else 1 + return {self._discriminator_key: discriminator, self._value_key: obj} + + +T = TypeVar("T") + + +class _DataclassStruct(Adapter): + """Converts dataclasses to/from `borsh_construct.CStruct`.""" + + def __init__(self, cstruct: CStruct, datacls: Type[T]) -> None: + """Init. + + Args: + cstruct: The underlying `CStruct`. + datacls: The dataclass type. + """ + super().__init__(cstruct) # type: ignore + self.datacls = datacls + + def _decode(self, obj: Container, context, path) -> T: # type: ignore + kwargs = {} + for key, value in obj.items(): + if key[0] != "_": + key_to_use = f"{key}_" if key in kwlist else key + kwargs[key_to_use] = value + return self.datacls(**kwargs) # type: ignore + + def _encode(self, obj: T, context, path) -> Dict[str, Any]: + if isinstance(obj, dict): + return obj + return asdict(obj) + + +BorshPubkey = BorshPubkeyAdapter() +"""Adapter for (de)serializing a public key.""" diff --git a/basics/anchorpy-main/src/anchorpy/cli.py b/basics/anchorpy-main/src/anchorpy/cli.py new file mode 100644 index 0000000..d9f069a --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/cli.py @@ -0,0 +1,142 @@ +import os +from contextlib import contextmanager +from pathlib import Path +from typing import Optional, cast + +import typer +from anchorpy_core.idl import Idl +from IPython import embed + +from anchorpy import create_workspace +from anchorpy.clientgen.accounts import gen_accounts +from anchorpy.clientgen.errors import gen_errors +from anchorpy.clientgen.instructions import gen_instructions +from anchorpy.clientgen.program_id import gen_program_id +from anchorpy.clientgen.types import gen_types +from anchorpy.template import INIT_TESTS + +app = typer.Typer() + + +@contextmanager +def _set_directory(path: Path): + """Set the cwd within the context. + + Args: + path (Path): The path to the cwd + + Yields: + None + """ # noqa: D202 + + origin = Path().absolute() + try: + os.chdir(path) + yield + finally: + os.chdir(origin) + + +def _search_upwards_for_project_root() -> Path: + """Search in the current dir and all directories above it for an Anchor.toml file. + + Returns: + The location of the first Anchor.toml file found + """ + search_dir = Path.cwd() + root = Path(search_dir.root) + + while search_dir != root: + attempt = search_dir / "Anchor.toml" + if attempt.exists(): + return search_dir + search_dir = search_dir.parent + + raise FileNotFoundError("Not in Anchor workspace.") + + +@app.callback() +def callback(): + """AnchorPy CLI.""" + + +@app.command() +def shell(): + """Start IPython shell with AnchorPy workspace object initialized. + + Note that you should run `anchor localnet` before `anchorpy shell`. + """ + path = _search_upwards_for_project_root() + workspace = create_workspace(path) # noqa: F841 + embed( + colors="neutral", + using="asyncio", + banner2="Hint: type `workspace` to see the Anchor workspace object.\n", + ) + + +@app.command() +def init( + program_name: str = typer.Argument(..., help="The name of the Anchor program.") +): + """Create a basic Python test file for an Anchor program. + + This does not replace `anchor init`, but rather should be + run after it. + + The test file will live at `tests/test_$PROGRAM_NAME.py`. + """ + file_contents = INIT_TESTS.format(program_name) + project_root = _search_upwards_for_project_root() + file_path = project_root / "tests" / f"test_{program_name}.py" + if file_path.exists(): + raise FileExistsError(file_path) + file_path.write_text(file_contents) + + +@app.command() +def client_gen( + idl: Path = typer.Argument(..., help="Anchor IDL file path"), + out: Path = typer.Argument(..., help="Output directory."), + program_id: Optional[str] = typer.Option( + None, help="Optional program ID to be included in the code" + ), + pdas: bool = typer.Option( + False, "--pdas", help="Auto-generate PDAs where possible." + ), +): + """Generate Python client code from the specified anchor IDL.""" + idl_obj = Idl.from_json(idl.read_text()) + if program_id is None: + idl_metadata = idl_obj.metadata + address_from_idl = ( + idl_metadata["address"] if isinstance(idl_metadata, dict) else None + ) + if address_from_idl is None: + typer.echo( + "No program ID found in IDL. Use the --program-id " + "option to set it manually." + ) + raise typer.Exit(code=1) + else: + program_id_to_use = cast(str, address_from_idl) + else: + program_id_to_use = program_id + + typer.echo("generating package...") + out.mkdir(exist_ok=True) + (out / "__init__.py").touch() + typer.echo("generating program_id.py...") + gen_program_id(program_id_to_use, out) + typer.echo("generating errors.py...") + gen_errors(idl_obj, out) + typer.echo("generating instructions...") + gen_instructions(idl_obj, out, pdas) + typer.echo("generating types...") + gen_types(idl_obj, out) + typer.echo("generating accounts...") + gen_accounts(idl_obj, out) + + +if __name__ == "__main__": + app() diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/__init__.py b/basics/anchorpy-main/src/anchorpy/clientgen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/accounts.py b/basics/anchorpy-main/src/anchorpy/clientgen/accounts.py new file mode 100644 index 0000000..3629989 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/accounts.py @@ -0,0 +1,303 @@ +from pathlib import Path +from typing import cast + +from anchorpy_core.idl import ( + Idl, + IdlField, + IdlTypeDefinition, + IdlTypeDefinitionTyStruct, +) +from autoflake import fix_code +from black import FileMode, format_str +from genpy import ( + Assign, + Collection, + For, + FromImport, + If, + Import, + ImportAs, + Raise, + Return, + Statement, + Suite, +) +from pyheck import snake + +from anchorpy.clientgen.common import ( + _field_from_decoded, + _field_from_json, + _field_to_json, + _idl_type_to_json_type, + _json_interface_name, + _layout_for_type, + _py_type_from_idl, + _sanitize, +) +from anchorpy.clientgen.genpy_extension import ( + Call, + ClassMethod, + Continue, + Dataclass, + Method, + NamedArg, + StrDict, + StrDictEntry, + TypedDict, + TypedParam, +) +from anchorpy.coder.accounts import _account_discriminator + + +def gen_accounts(idl: Idl, root: Path) -> None: + accounts = idl.accounts + if accounts is None or not accounts: + return + accounts_dir = root / "accounts" + accounts_dir.mkdir(exist_ok=True) + gen_index_file(idl, accounts_dir) + accounts_dict = gen_accounts_code(idl, accounts_dir) + for path, code in accounts_dict.items(): + formatted = format_str(code, mode=FileMode()) + fixed = fix_code(formatted, remove_all_unused_imports=True) + path.write_text(fixed) + + +def gen_index_file(idl: Idl, accounts_dir: Path) -> None: + code = gen_index_code(idl) + formatted = format_str(code, mode=FileMode()) + (accounts_dir / "__init__.py").write_text(formatted) + + +def gen_index_code(idl: Idl) -> str: + imports: list[FromImport] = [] + for acc in idl.accounts: + acc_name = _sanitize(acc.name) + members = [ + acc_name, + _json_interface_name(acc_name), + ] + module_name = _sanitize(snake(acc.name)) + imports.append(FromImport(f".{module_name}", members)) + return str(Collection(imports)) + + +def gen_accounts_code(idl: Idl, accounts_dir: Path) -> dict[Path, str]: + res = {} + for acc in idl.accounts: + filename = f"{_sanitize(snake(acc.name))}.py" + path = accounts_dir / filename + code = gen_account_code(acc, idl) + res[path] = code + return res + + +def gen_account_code(acc: IdlTypeDefinition, idl: Idl) -> str: + base_imports = [ + Import("typing"), + FromImport("dataclasses", ["dataclass"]), + FromImport("construct", ["Construct"]), + FromImport("solders.pubkey", ["Pubkey"]), + FromImport("solana.rpc.async_api", ["AsyncClient"]), + FromImport("solana.rpc.commitment", ["Commitment"]), + ImportAs("borsh_construct", "borsh"), + FromImport("anchorpy.coder.accounts", ["ACCOUNT_DISCRIMINATOR_SIZE"]), + FromImport("anchorpy.error", ["AccountInvalidDiscriminator"]), + FromImport("anchorpy.utils.rpc", ["get_multiple_accounts"]), + FromImport( + "anchorpy.borsh_extension", ["BorshPubkey", "EnumForCodegen", "COption"] + ), + FromImport("..program_id", ["PROGRAM_ID"]), + ] + imports = ( + [*base_imports, FromImport("..", ["types"])] if idl.types else base_imports + ) + fields_interface_params: list[TypedParam] = [] + json_interface_params: list[TypedParam] = [] + ty = cast(IdlTypeDefinitionTyStruct, acc.ty) + fields = ty.fields + name = _sanitize(acc.name) + json_interface_name = _json_interface_name(name) + layout_items: list[str] = [] + init_body_assignments: list[Assign] = [] + decode_body_entries: list[NamedArg] = [] + to_json_entries: list[StrDictEntry] = [] + from_json_entries: list[NamedArg] = [] + for field in fields: + field_name = _sanitize(snake(field.name)) + fields_interface_params.append( + TypedParam( + field_name, + _py_type_from_idl( + idl=idl, + ty=field.ty, + types_relative_imports=False, + use_fields_interface_for_struct=False, + ), + ) + ) + json_interface_params.append( + TypedParam( + field_name, + _idl_type_to_json_type(ty=field.ty, types_relative_imports=False), + ) + ) + layout_items.append( + _layout_for_type( + idl=idl, ty=field.ty, name=field_name, types_relative_imports=False + ) + ) + init_body_assignments.append( + Assign(f"self.{field_name}", f'fields["{field_name}"]') + ) + decode_body_entries.append( + NamedArg( + field_name, + _field_from_decoded( + idl=idl, + ty=IdlField(name=snake(field.name), docs=None, ty=field.ty), + types_relative_imports=False, + val_prefix="dec.", + ), + ) + ) + to_json_entries.append( + StrDictEntry(field_name, _field_to_json(idl, field, "self.")) + ) + from_json_entries.append( + NamedArg( + field_name, + _field_from_json(idl=idl, ty=field, types_relative_imports=False), + ) + ) + json_interface = TypedDict(json_interface_name, json_interface_params) + discriminator_assignment = Assign( + "discriminator: typing.ClassVar", _account_discriminator(name) + ) + layout_assignment = Assign( + "layout: typing.ClassVar", f"borsh.CStruct({','.join(layout_items)})" + ) + fetch_method = ClassMethod( + "fetch", + [ + TypedParam("conn", "AsyncClient"), + TypedParam("address", "Pubkey"), + TypedParam("commitment", "typing.Optional[Commitment] = None"), + TypedParam("program_id", "Pubkey = PROGRAM_ID"), + ], + Suite( + [ + Assign( + "resp", + "await conn.get_account_info(address, commitment=commitment)", + ), + Assign("info", "resp.value"), + If("info is None", Return("None")), + If( + "info.owner != program_id", + Raise('ValueError("Account does not belong to this program")'), + ), + Assign("bytes_data", "info.data"), + Return("cls.decode(bytes_data)"), + ] + ), + f'typing.Optional["{name}"]', + is_async=True, + ) + account_does_not_belong_raise = Raise( + 'ValueError("Account does not belong to this program")' + ) + fetch_multiple_return_type = f'typing.List[typing.Optional["{name}"]]' + fetch_multiple_method = ClassMethod( + "fetch_multiple", + [ + TypedParam("conn", "AsyncClient"), + TypedParam("addresses", "list[Pubkey]"), + TypedParam("commitment", "typing.Optional[Commitment] = None"), + TypedParam("program_id", "Pubkey = PROGRAM_ID"), + ], + Suite( + [ + Assign( + "infos", + ( + "await get_multiple_accounts" + "(conn, addresses,commitment=commitment)" + ), + ), + Assign(f"res: {fetch_multiple_return_type}", "[]"), + For( + "info", + "infos", + Suite( + [ + If( + "info is None", + Suite([Statement("res.append(None)"), Continue()]), + ), + If( + "info.account.owner != program_id", + account_does_not_belong_raise, + ), + Statement("res.append(cls.decode(info.account.data))"), + ] + ), + ), + Return("res"), + ] + ), + f'typing.List[typing.Optional["{name}"]]', + is_async=True, + ) + decode_body_end = Call("cls", decode_body_entries) + account_invalid_raise = Raise( + 'AccountInvalidDiscriminator("The discriminator for this account is invalid")' + ) + decode_method = ClassMethod( + "decode", + [TypedParam("data", "bytes")], + Suite( + [ + If( + "data[:ACCOUNT_DISCRIMINATOR_SIZE] != cls.discriminator", + account_invalid_raise, + ), + Assign( + "dec", f"{name}.layout.parse(data[ACCOUNT_DISCRIMINATOR_SIZE:])" + ), + Return(decode_body_end), + ] + ), + f'"{name}"', + ) + to_json_body = StrDict(to_json_entries) + to_json_method = Method("to_json", [], Return(to_json_body), json_interface_name) + from_json_body = Call("cls", from_json_entries) + from_json_method = ClassMethod( + "from_json", + [TypedParam("obj", json_interface_name)], + Return(from_json_body), + f'"{name}"', + ) + klass = Dataclass( + name, + [ + discriminator_assignment, + layout_assignment, + *fields_interface_params, + fetch_method, + fetch_multiple_method, + decode_method, + to_json_method, + from_json_method, + ], + ) + return str( + Collection( + [ + *imports, + json_interface, + klass, + ] + ) + ) diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/common.py b/basics/anchorpy-main/src/anchorpy/clientgen/common.py new file mode 100644 index 0000000..f12b268 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/common.py @@ -0,0 +1,636 @@ +"""Code generation utilities.""" +import keyword +from typing import Optional + +from anchorpy_core.idl import ( + Idl, + IdlField, + IdlType, + IdlTypeArray, + IdlTypeDefined, + IdlTypeDefinitionTyStruct, + IdlTypeOption, + IdlTypeSimple, + IdlTypeVec, +) +from pyheck import snake + +_DEFAULT_DEFINED_TYPES_PREFIX = "types." + +INT_TYPES = { + IdlTypeSimple.U8, + IdlTypeSimple.I8, + IdlTypeSimple.U16, + IdlTypeSimple.I16, + IdlTypeSimple.U32, + IdlTypeSimple.I32, + IdlTypeSimple.U64, + IdlTypeSimple.I64, + IdlTypeSimple.U128, + IdlTypeSimple.I128, +} +FLOAT_TYPES = {IdlTypeSimple.F32, IdlTypeSimple.F64} +NUMBER_TYPES = INT_TYPES | FLOAT_TYPES + + +def _fields_interface_name(type_name: str) -> str: + return f"{type_name}Fields" + + +def _value_interface_name(type_name: str) -> str: + return f"{type_name}Value" + + +def _kind_interface_name(type_name: str) -> str: + return f"{type_name}Kind" + + +def _json_interface_name(type_name: str) -> str: + return f"{type_name}JSON" + + +def _sanitize(name: str) -> str: + return f"{name}_" if keyword.iskeyword(name) else name + + +def _py_type_from_idl( + idl: Idl, + ty: IdlType, + types_relative_imports: bool, + use_fields_interface_for_struct: bool, +) -> str: + if isinstance(ty, IdlTypeVec): + inner_type = _py_type_from_idl( + idl=idl, + ty=ty.vec, + types_relative_imports=types_relative_imports, + use_fields_interface_for_struct=use_fields_interface_for_struct, + ) + return f"list[{inner_type}]" + if isinstance(ty, IdlTypeOption): + inner_type = _py_type_from_idl( + idl=idl, + ty=ty.option, + types_relative_imports=types_relative_imports, + use_fields_interface_for_struct=use_fields_interface_for_struct, + ) + return f"typing.Optional[{inner_type}]" + if isinstance(ty, IdlTypeDefined): + defined = _sanitize(ty.defined) + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = {"u64": "int", "Pubkey": "Pubkey"}[maybe_coption_split[1][:-1]] + return f"typing.Optional[{inner_type}]" + if defined == "&'astr": + return "str" + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + typedef_type = filtered[0].ty + module = _sanitize(snake(ty.defined)) + if isinstance(typedef_type, IdlTypeDefinitionTyStruct): + name = ( + _fields_interface_name(ty.defined) + if use_fields_interface_for_struct + else defined + ) + else: + # enum + name = _kind_interface_name(ty.defined) + return f"{defined_types_prefix}{module}.{name}" + if isinstance(ty, IdlTypeArray): + inner_type = _py_type_from_idl( + idl=idl, + ty=ty.array[0], + types_relative_imports=types_relative_imports, + use_fields_interface_for_struct=use_fields_interface_for_struct, + ) + return f"list[{inner_type}]" + if ty in {IdlTypeSimple.Bool, IdlTypeSimple.Bytes}: + return str(ty).replace("IdlTypeSimple.", "").lower() + if ty in INT_TYPES: + return "int" + if ty in FLOAT_TYPES: + return "float" + if ty == IdlTypeSimple.String: + return "str" + if ty == IdlTypeSimple.PublicKey: + return "Pubkey" + raise ValueError(f"Unrecognized type: {ty}") + + +def _layout_for_type( + idl: Idl, + ty: IdlType, + types_relative_imports: bool, + name: Optional[str] = None, +) -> str: + if ty == IdlTypeSimple.PublicKey: + inner = "BorshPubkey" + elif isinstance(ty, IdlTypeSimple): + inner = str(ty).replace("IdlTypeSimple", "borsh") + elif isinstance(ty, IdlTypeVec): + layout = _layout_for_type( + idl=idl, ty=ty.vec, types_relative_imports=types_relative_imports + ) + cast_layout = f"typing.cast(Construct, {layout})" + inner = f"borsh.Vec({cast_layout})" + elif isinstance(ty, IdlTypeOption): + layout = _layout_for_type( + idl=idl, ty=ty.option, types_relative_imports=types_relative_imports + ) + inner = f"borsh.Option({layout})" + elif isinstance(ty, IdlTypeDefined): + defined = _sanitize(ty.defined) + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + layout_str = maybe_coption_split[1][:-1] + layout = {"u64": "borsh.U64", "Pubkey": "BorshPubkey"}[layout_str] + inner = f"COption({layout})" + elif defined == "&'astr": + return "borsh.String" + else: + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + typedef_type = filtered[0].ty + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + module = snake(defined) + inner = ( + f"{defined_types_prefix}{module}.{defined}.layout" + if isinstance(typedef_type, IdlTypeDefinitionTyStruct) + else f"{defined_types_prefix}{module}.layout" + ) + elif isinstance(ty, IdlTypeArray): + layout = _layout_for_type( + idl=idl, ty=ty.array[0], types_relative_imports=types_relative_imports + ) + inner = f"{layout}[{ty.array[1]}]" + else: + raise ValueError(f"Unrecognized type: {ty}") + + if name is None: + return inner + return f'"{name}" / {inner}' + + +def _maybe_none(to_check: str, if_not_none: str) -> str: + return f"(None if {to_check} is None else {if_not_none})" + + +def _field_to_encodable( + idl: Idl, + ty: IdlField, + types_relative_imports: bool, + val_prefix: str = "", + val_suffix: str = "", + convert_case: bool = True, +) -> str: + ty_type = ty.ty + maybe_converted = snake(ty.name) if convert_case else ty.name + ty_name = _sanitize(maybe_converted) + if isinstance(ty_type, IdlTypeVec): + map_body = _field_to_encodable( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.vec), + val_prefix="", + types_relative_imports=types_relative_imports, + val_suffix="", + ) + # skip mapping when not needed + if map_body == "item": + return f"{val_prefix}{ty_name}{val_suffix}" + return f"list(map(lambda item: {map_body}, {val_prefix}{ty_name}{val_suffix}))" + if isinstance(ty_type, IdlTypeOption): + encodable = _field_to_encodable( + idl=idl, + ty=IdlField(ty_name, docs=None, ty=ty_type.option), + val_prefix=val_prefix, + types_relative_imports=types_relative_imports, + val_suffix=val_suffix, + convert_case=convert_case, + ) + if encodable == f"{val_prefix}{ty_name}{val_suffix}": + return encodable + return _maybe_none(f"{val_prefix}{ty_name}{val_suffix}", encodable) + if isinstance(ty_type, IdlTypeDefined): + defined = _sanitize(ty_type.defined) + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = maybe_coption_split[1][:-1] + encodable = _field_to_encodable( + idl=idl, + ty=IdlField( + ty_name, + docs=None, + ty={"u64": IdlTypeSimple.U64, "Pubkey": IdlTypeSimple.PublicKey}[ + inner_type + ], + ), + val_prefix=val_prefix, + types_relative_imports=types_relative_imports, + val_suffix=val_suffix, + convert_case=convert_case, + ) + if encodable == f"{val_prefix}{ty_name}{val_suffix}": + return encodable + return _maybe_none(f"{val_prefix}{ty_name}{val_suffix}", encodable) + elif defined == "&'astr": + return f"{val_prefix}{ty_name}{val_suffix}" + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + typedef_type = filtered[0].ty + if isinstance(typedef_type, IdlTypeDefinitionTyStruct): + val_full_name = f"{val_prefix}{ty_name}{val_suffix}" + return f"{val_full_name}.to_encodable()" + return f"{val_prefix}{ty_name}{val_suffix}.to_encodable()" + if isinstance(ty_type, IdlTypeArray): + map_body = _field_to_encodable( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.array[0]), + val_prefix="", + types_relative_imports=types_relative_imports, + val_suffix="", + ) + # skip mapping when not needed + if map_body == "item": + return f"{val_prefix}{ty_name}{val_suffix}" + return f"list(map(lambda item: {map_body}, {val_prefix}{ty_name}{val_suffix}))" + if ty_type in { + IdlTypeSimple.Bool, + *NUMBER_TYPES, + IdlTypeSimple.String, + IdlTypeSimple.PublicKey, + IdlTypeSimple.Bytes, + }: + return f"{val_prefix}{ty_name}{val_suffix}" + raise ValueError(f"Unrecognized type: {ty_type}") + + +def _field_from_decoded( + idl: Idl, ty: IdlField, types_relative_imports: bool, val_prefix: str = "" +) -> str: + ty_type = ty.ty + ty_name = _sanitize(ty.name) + if isinstance(ty_type, IdlTypeVec): + map_body = _field_from_decoded( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.vec), + val_prefix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return f"{val_prefix}{ty_name}" + return f"list(map(lambda item: {map_body}, {val_prefix}{ty_name}))" + if isinstance(ty_type, IdlTypeOption): + decoded = _field_from_decoded( + idl=idl, + ty=IdlField(ty_name, docs=None, ty=ty_type.option), + types_relative_imports=types_relative_imports, + val_prefix=val_prefix, + ) + # skip coercion when not needed + if decoded == f"{val_prefix}{ty_name}": + return decoded + return _maybe_none(f"{val_prefix}{ty_name}", decoded) + if isinstance(ty_type, IdlTypeDefined): + defined = _sanitize(ty_type.defined) + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = maybe_coption_split[1][:-1] + decoded = _field_from_decoded( + idl=idl, + ty=IdlField( + ty_name, + docs=None, + ty={"u64": IdlTypeSimple.U64, "Pubkey": IdlTypeSimple.PublicKey}[ + inner_type + ], + ), + types_relative_imports=types_relative_imports, + val_prefix=val_prefix, + ) + # skip coercion when not needed + if decoded == f"{val_prefix}{ty_name}": + return decoded + return _maybe_none(f"{val_prefix}{ty_name}", decoded) + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + typedef_type = filtered[0].ty + from_decoded_func_path = ( + f"{snake(defined)}.{defined}" + if isinstance(typedef_type, IdlTypeDefinitionTyStruct) + else f"{snake(defined)}" + ) + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + full_func_path = f"{defined_types_prefix}{from_decoded_func_path}" + from_decoded_arg = f"{val_prefix}{ty_name}" + return f"{full_func_path}.from_decoded({from_decoded_arg})" + if isinstance(ty_type, IdlTypeArray): + map_body = _field_from_decoded( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.array[0]), + val_prefix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return f"{val_prefix}{ty_name}" + return f"list(map(lambda item: {map_body}, {val_prefix}{ty_name}))" + if ty_type in { + IdlTypeSimple.Bool, + *NUMBER_TYPES, + IdlTypeSimple.String, + IdlTypeSimple.PublicKey, + IdlTypeSimple.Bytes, + }: + return f"{val_prefix}{ty_name}" + raise ValueError(f"Unrecognized type: {ty_type}") + + +def _struct_field_initializer( + idl: Idl, + field: IdlField, + types_relative_imports: bool, + prefix: str = 'fields["', + suffix: str = '"]', +) -> str: + field_type = field.ty + field_name = _sanitize(snake(field.name)) + if isinstance(field_type, IdlTypeDefined): + defined = _sanitize(field_type.defined) + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + typedef_type = filtered[0].ty + if isinstance(typedef_type, IdlTypeDefinitionTyStruct): + module = snake(defined) + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + obj_name = f"{defined_types_prefix}{module}.{defined}" + return f"{obj_name}(**{prefix}{field_name}{suffix})" + return f"{prefix}{field_name}{suffix}" + if isinstance(field_type, IdlTypeOption): + initializer = _struct_field_initializer( + idl=idl, + field=IdlField(field_name, docs=None, ty=field_type.option), + prefix=prefix, + suffix=suffix, + types_relative_imports=types_relative_imports, + ) + # skip coercion when not needed + if initializer == f"{prefix}{field_name}{suffix}": + return initializer + return _maybe_none(f"{prefix}{field_name}{suffix}", initializer) + if isinstance(field_type, IdlTypeArray): + map_body = _struct_field_initializer( + idl=idl, + field=IdlField("item", docs=None, ty=field_type.array[0]), + prefix="", + suffix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return f"{prefix}{field_name}{suffix}" + return f"list(map(lambda item: {map_body}, {prefix}{field_name}{suffix}))" + if isinstance(field_type, IdlTypeVec): + map_body = _struct_field_initializer( + idl=idl, + field=IdlField("item", docs=None, ty=field_type.vec), + prefix="", + suffix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return f"{prefix}{field_name}{suffix}" + return f"list(map(lambda item: {map_body}, {prefix}{field_name}{suffix}))" + if field_type in { + IdlTypeSimple.Bool, + *NUMBER_TYPES, + IdlTypeSimple.String, + IdlTypeSimple.PublicKey, + IdlTypeSimple.Bytes, + }: + return f"{prefix}{field_name}{suffix}" + raise ValueError(f"Unrecognized type: {field_type}") + + +def _field_to_json( + idl: Idl, + ty: IdlField, + val_prefix: str = "", + val_suffix: str = "", + convert_case: bool = True, +) -> str: + ty_type = ty.ty + maybe_converted = _sanitize(snake(ty.name)) if convert_case else _sanitize(ty.name) + var_name = f"{val_prefix}{maybe_converted}{val_suffix}" + if ty_type == IdlTypeSimple.PublicKey: + return f"str({var_name})" + if isinstance(ty_type, IdlTypeVec): + map_body = _field_to_json(idl, IdlField("item", docs=None, ty=ty_type.vec)) + # skip mapping when not needed + if map_body == "item": + return var_name + return f"list(map(lambda item: {map_body}, {var_name}))" + if isinstance(ty_type, IdlTypeArray): + map_body = _field_to_json(idl, IdlField("item", docs=None, ty=ty_type.array[0])) + # skip mapping when not needed + if map_body == "item": + return var_name + return f"list(map(lambda item: {map_body}, {var_name}))" + if isinstance(ty_type, IdlTypeOption): + value = _field_to_json( + idl, + IdlField(ty.name, docs=None, ty=ty_type.option), + val_prefix, + val_suffix, + convert_case=convert_case, + ) + # skip coercion when not needed + if value == var_name: + return value + return _maybe_none(var_name, value) + if isinstance(ty_type, IdlTypeDefined): + defined = ty_type.defined + filtered = [t for t in idl.types if t.name == defined] + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = maybe_coption_split[1][:-1] + value = _field_to_json( + idl, + IdlField( + ty.name, + docs=None, + ty={"u64": IdlTypeSimple.U64, "Pubkey": IdlTypeSimple.PublicKey}[ + inner_type + ], + ), + val_prefix, + val_suffix, + convert_case=convert_case, + ) + # skip coercion when not needed + if value == var_name: + return value + return _maybe_none(var_name, value) + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + return f"{var_name}.to_json()" + if ty_type == IdlTypeSimple.Bytes: + return f"list({var_name})" + if ty_type in { + IdlTypeSimple.Bool, + *NUMBER_TYPES, + IdlTypeSimple.String, + }: + return var_name + raise ValueError(f"Unrecognized type: {ty_type}") + + +def _idl_type_to_json_type(ty: IdlType, types_relative_imports: bool) -> str: + if isinstance(ty, IdlTypeVec): + inner = _idl_type_to_json_type( + ty=ty.vec, types_relative_imports=types_relative_imports + ) + return f"list[{inner}]" + if isinstance(ty, IdlTypeArray): + inner = _idl_type_to_json_type( + ty=ty.array[0], types_relative_imports=types_relative_imports + ) + return f"list[{inner}]" + if isinstance(ty, IdlTypeOption): + inner = _idl_type_to_json_type( + ty=ty.option, types_relative_imports=types_relative_imports + ) + return f"typing.Optional[{inner}]" + if isinstance(ty, IdlTypeDefined): + defined = ty.defined + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = {"u64": "int", "Pubkey": "str"}[maybe_coption_split[1][:-1]] + return f"typing.Optional[{inner_type}]" + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + module = _sanitize(snake(defined)) + return f"{defined_types_prefix}{module}.{_json_interface_name(defined)}" + if ty == IdlTypeSimple.Bool: + return "bool" + if ty in INT_TYPES: + return "int" + if ty in FLOAT_TYPES: + return "float" + if ty == IdlTypeSimple.Bytes: + return "list[int]" + if ty in {IdlTypeSimple.String, IdlTypeSimple.PublicKey}: + return "str" + raise ValueError(f"Unrecognized type: {ty}") + + +def _field_from_json( + idl: Idl, + ty: IdlField, + types_relative_imports: bool, + param_prefix: str = 'obj["', + param_suffix: str = '"]', +) -> str: + ty_type = ty.ty + ty_name_snake_unsanitized = snake(ty.name) + ty_name = _sanitize(ty_name_snake_unsanitized) + var_name = f"{param_prefix}{ty_name_snake_unsanitized}{param_suffix}" + if ty_type == IdlTypeSimple.PublicKey: + return f"Pubkey.from_string({var_name})" + if isinstance(ty_type, IdlTypeVec): + map_body = _field_from_json( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.vec), + param_prefix="", + param_suffix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return var_name + return f"list(map(lambda item: {map_body}, {var_name}))" + if isinstance(ty_type, IdlTypeArray): + map_body = _field_from_json( + idl=idl, + ty=IdlField("item", docs=None, ty=ty_type.array[0]), + param_prefix="", + param_suffix="", + types_relative_imports=types_relative_imports, + ) + # skip mapping when not needed + if map_body == "item": + return var_name + return f"list(map(lambda item: {map_body}, {var_name}))" + if isinstance(ty_type, IdlTypeOption): + inner = _field_from_json( + idl=idl, + ty=IdlField(ty_name, docs=None, ty=ty_type.option), + param_prefix=param_prefix, + param_suffix=param_suffix, + types_relative_imports=types_relative_imports, + ) + # skip coercion when not needed + if inner == var_name: + return inner + return _maybe_none(var_name, inner) + if isinstance(ty_type, IdlTypeDefined): + from_json_arg = var_name + defined = _sanitize(ty_type.defined) + maybe_coption_split = defined.split("COption<") + if len(maybe_coption_split) == 2: + inner_type = maybe_coption_split[1][:-1] + inner = _field_from_json( + idl=idl, + ty=IdlField( + ty_name, + docs=None, + ty={"u64": IdlTypeSimple.U64, "Pubkey": IdlTypeSimple.PublicKey}[ + inner_type + ], + ), + param_prefix=param_prefix, + param_suffix=param_suffix, + types_relative_imports=types_relative_imports, + ) + # skip coercion when not needed + if inner == var_name: + return inner + return _maybe_none(var_name, inner) + defined_snake = _sanitize(snake(ty_type.defined)) + filtered = [t for t in idl.types if _sanitize(t.name) == defined] + typedef_type = filtered[0].ty + from_json_func_path = ( + f"{defined_snake}.{defined}" + if isinstance(typedef_type, IdlTypeDefinitionTyStruct) + else f"{defined_snake}" + ) + defined_types_prefix = ( + "" if types_relative_imports else _DEFAULT_DEFINED_TYPES_PREFIX + ) + full_func_path = f"{defined_types_prefix}{from_json_func_path}" + return f"{full_func_path}.from_json({from_json_arg})" + if ty_type == IdlTypeSimple.Bytes: + return f"bytes({var_name})" + if ty_type in { + IdlTypeSimple.Bool, + *NUMBER_TYPES, + IdlTypeSimple.String, + }: + return var_name + raise ValueError(f"Unrecognized type: {ty_type}") diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/errors.py b/basics/anchorpy-main/src/anchorpy/clientgen/errors.py new file mode 100644 index 0000000..a16accf --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/errors.py @@ -0,0 +1,245 @@ +from pathlib import Path + +from anchorpy_core.idl import Idl, IdlErrorCode +from autoflake import fix_code +from black import FileMode, format_str +from genpy import ( + Assign, + Collection, + FromImport, + If, + Import, + Return, + Statement, + Suite, +) +from genpy import Function as UntypedFunction + +from anchorpy.clientgen.common import _sanitize +from anchorpy.clientgen.genpy_extension import ( + Class, + Function, + InitMethod, + IntDict, + IntDictEntry, + TypedParam, + Union, +) +from anchorpy.error import LangErrorMessage, _LangErrorCode + + +def gen_from_code_fn(has_custom_errors: bool) -> Function: + from_code_body = Return( + "custom.from_code(code) if code >= 6000 else anchor.from_code(code)" + if has_custom_errors + else "anchor.from_code(code)" + ) + from_code_return_type = ( + Union(["custom.CustomError", "anchor.AnchorError", "None"]) + if has_custom_errors + else "typing.Optional[anchor.AnchorError]" + ) + return Function( + "from_code", + [TypedParam("code", "int")], + from_code_body, + str(from_code_return_type), + ) + + +def gen_from_tx_error_fn(has_custom_errors: bool) -> Function: + err_info_assign = Assign("err_info", "error.args[0]") + err_code_assign = Assign("extracted", "extract_code_and_logs(err_info, PROGRAM_ID)") + null_code_check = If("extracted is None", Return(None)) + final_return = Return("from_code(extracted[0])") + fn_body = Suite( + [ + err_info_assign, + err_code_assign, + null_code_check, + final_return, + ] + ) + return_type = ( + "typing.Union[anchor.AnchorError, custom.CustomError, None]" + if has_custom_errors + else "typing.Optional[anchor.AnchorError]" + ) + return Function( + "from_tx_error", + [TypedParam("error", "RPCException")], + fn_body, + return_type, + ) + + +def gen_custom_errors_code(errors: list[IdlErrorCode]) -> str: + typing_import = Import("typing") + error_import = FromImport( + "anchorpy.error", ["ProgramError", "extract_code_and_logs"] + ) + error_names: list[str] = [] + classes: list[Class] = [] + error_map_entries: list[IntDictEntry] = [] + for error in errors: + code = error.code + name = _sanitize(error.name) + maybe_msg = error.msg + msg = None if maybe_msg is None else f'"{maybe_msg}"' + init_body = Statement(f"super().__init__({code}, {msg})") + attrs = [ + InitMethod([], init_body), + Assign("code", code), + Assign("name", f'"{name}"'), + Assign("msg", msg), + ] + klass = Class(name=name, bases=["ProgramError"], attributes=attrs) + classes.append(klass) + error_names.append(name) + error_map_entries.append(IntDictEntry(code, f"{name}()")) + type_alias = Assign("CustomError", Union(error_names)) + error_map = Assign( + "CUSTOM_ERROR_MAP: dict[int, CustomError]", IntDict(error_map_entries) + ) + from_code_body = Suite( + [ + Assign("maybe_err", "CUSTOM_ERROR_MAP.get(code)"), + If("maybe_err is None", Return("None")), + Return("maybe_err"), + ] + ) + from_code_fn = Function( + "from_code", + [TypedParam("code", "int")], + from_code_body, + "typing.Optional[CustomError]", + ) + return str( + Collection( + [typing_import, error_import, *classes, type_alias, error_map, from_code_fn] + ) + ) + + +def gen_custom_errors(idl: Idl, errors_dir: Path) -> None: + errors = idl.errors + if errors is None or not errors: + return + code = gen_custom_errors_code(errors) + formatted = format_str(code, mode=FileMode()) + fixed = fix_code(formatted, remove_all_unused_imports=True) + (errors_dir / "custom.py").with_suffix(".py").write_text(fixed) + + +def gen_anchor_errors_code() -> str: + typing_import = Import("typing") + error_import = FromImport("anchorpy.error", ["ProgramError"]) + error_names: list[str] = [] + classes: list[Class] = [] + error_map_entries: list[IntDictEntry] = [] + for variant in _LangErrorCode: + name = _sanitize(variant.name) + code = variant.value + maybe_msg = LangErrorMessage.get(variant) + msg = None if maybe_msg is None else f'"{maybe_msg}"' + init_body = Statement(f"super().__init__({code}, {msg})") + attrs = [ + UntypedFunction("__init__", ["self"], init_body), + Assign("code", code), + Assign("name", f'"{name}"'), + Assign("msg", msg), + ] + klass = Class(name=name, bases=["ProgramError"], attributes=attrs) + classes.append(klass) + error_names.append(name) + error_map_entries.append(IntDictEntry(code, f"{name}()")) + type_alias = Assign("AnchorError", Union(error_names)) + error_map = Assign( + "ANCHOR_ERROR_MAP: dict[int, AnchorError]", + IntDict(error_map_entries), + ) + from_code_body = Suite( + [ + Assign("maybe_err", "ANCHOR_ERROR_MAP.get(code)"), + If("maybe_err is None", Return("None")), + Return("maybe_err"), + ] + ) + from_code_fn = Function( + "from_code", + [TypedParam("code", "int")], + from_code_body, + "typing.Optional[AnchorError]", + ) + return str( + Collection( + [typing_import, error_import, *classes, type_alias, error_map, from_code_fn] + ) + ) + + +def gen_anchor_errors(errors_dir: Path) -> None: + code = gen_anchor_errors_code() + formatted = format_str(code, mode=FileMode()) + (errors_dir / "anchor").with_suffix(".py").write_text(formatted) + + +def gen_index_code(idl: Idl) -> str: + has_custom_errors = bool(idl.errors) + typing_import = Import("typing") + rpc_exception_import = FromImport("solana.rpc.core", ["RPCException"]) + tx_status_import = FromImport( + "solders.transaction_status", + ["InstructionErrorCustom", "TransactionErrorInstructionError"], + ) + preflight_error_import = FromImport( + "solders.rpc.errors", ["SendTransactionPreflightFailureMessage"] + ) + extract_code_and_logs_import = FromImport( + "anchorpy.error", ["extract_code_and_logs"] + ) + program_id_import = FromImport("..program_id", ["PROGRAM_ID"]) + anchor_import = FromImport(".", ["anchor"]) + re_import = Import("re") + base_import_lines = [ + typing_import, + re_import, + tx_status_import, + rpc_exception_import, + preflight_error_import, + extract_code_and_logs_import, + program_id_import, + anchor_import, + ] + custom_import_lines = [FromImport(".", ["custom"])] if has_custom_errors else [] + import_lines = base_import_lines + custom_import_lines + from_code_fn = gen_from_code_fn(has_custom_errors) + error_re_line = Assign( + "error_re", r're.compile(r"Program (\w+) failed: custom program error: (\w+)")' + ) + from_tx_error_fn = gen_from_tx_error_fn(has_custom_errors) + return str( + Collection( + [ + *import_lines, + from_code_fn, + error_re_line, + from_tx_error_fn, + ] + ) + ) + + +def gen_index_file(idl: Idl, errors_dir: Path) -> None: + code = gen_index_code(idl) + path = errors_dir / "__init__.py" + formatted = format_str(code, mode=FileMode()) + path.write_text(formatted) + + +def gen_errors(idl: Idl, root: Path) -> None: + errors_dir = root / "errors" + errors_dir.mkdir(exist_ok=True) + gen_index_file(idl, errors_dir) + gen_custom_errors(idl, errors_dir) + gen_anchor_errors(errors_dir) diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/genpy_extension.py b/basics/anchorpy-main/src/anchorpy/clientgen/genpy_extension.py new file mode 100644 index 0000000..40c140a --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/genpy_extension.py @@ -0,0 +1,239 @@ +from typing import Iterator, Optional +from typing import Union as TypingUnion + +from genpy import ( + Assign, + FromImport, + Generable, + Suite, +) +from genpy import Class as BrokenClass +from genpy import Function as FunctionOriginal + + +class Class(BrokenClass): + def generate(self) -> Iterator[str]: + bases = self.bases + if not bases: + bases = [] + + yield "class {}({}):".format(self.name, ", ".join(bases)) + if self.attributes: + for f in self.attributes: + yield from (" " + f_line for f_line in f.generate()) + else: + yield " pass" + + +class TypedParam(Generable): + def __init__(self, name: str, type_: Optional[str]) -> None: + self.name = name + self.type = type_ + + def generate(self) -> Iterator[str]: + if self.type is None: + yield self.name + else: + yield f"{self.name}: {self.type}" + + +class Break(Generable): + def generate(self): + yield "break" + + +class Continue(Generable): + def generate(self): + yield "continue" + + +class Union(Generable): + def __init__(self, members: list[str]) -> None: + self.members = members + + def generate(self) -> Iterator[str]: + joined = ",".join(self.members) + yield f"typing.Union[{joined}]" + + +class Tuple(Generable): + def __init__(self, members: list[str]) -> None: + self.members = members + + def generate(self) -> Iterator[str]: + joined = ",".join(self.members) + yield f"({joined},)" + + +class List(Generable): + def __init__(self, members: list[str]) -> None: + self.members = members + + def generate(self) -> Iterator[str]: + joined = ",".join(self.members) + yield f"[{joined}]" + + +class TupleTypeAlias(Generable): + def __init__(self, name: str, members: list[str]) -> None: + self.name = name + self.members = members + + def generate(self) -> Iterator[str]: + yield str(Assign(self.name, f"tuple{List(self.members)}")) + + +class StrDictEntry(Generable): + def __init__(self, key: str, val: TypingUnion[str, "StrDict"]) -> None: + self.key = key + self.val = val + + def generate(self) -> Iterator[str]: + yield f'"{self.key}": {self.val},' + + +class NamedArg(Generable): + def __init__(self, key: str, val: str) -> None: + self.key = key + self.val = val + + def generate(self) -> Iterator[str]: + yield f"{self.key}={self.val}," + + +class Call(Generable): + def __init__(self, func: str, args: list[NamedArg]) -> None: + self.func = func + self.args = args + + def generate(self) -> Iterator[str]: + formatted_args = "".join(str(arg) for arg in self.args) + yield f"{self.func}({formatted_args})" + + +class StrDict(Generable): + def __init__(self, items: list[StrDictEntry]) -> None: + self.items = items + + def generate(self) -> Iterator[str]: + formatted_items = "".join(str(item) for item in self.items) + yield "{" + formatted_items + "}" + + +class IntDictEntry(Generable): + def __init__(self, key: int, val: str) -> None: + self.key = key + self.val = val + + def generate(self) -> Iterator[str]: + yield f"{self.key}: {self.val}," + + +class IntDict(Generable): + def __init__(self, items: list[IntDictEntry]) -> None: + self.items = items + + def generate(self) -> Iterator[str]: + formatted_items = "".join(str(item) for item in self.items) + yield "{" + formatted_items + "}" + + +class Function(FunctionOriginal): + def __init__( + self, + name: str, + args: list[TypedParam], + body: Generable, + return_type: str, + decorators: tuple[str, ...] = (), + is_async: bool = False, + ) -> None: + super().__init__(name, args, body, decorators) + self.return_type = return_type + self.is_async = is_async + + def generate(self) -> Iterator[str]: + yield from self.decorators + arg_strings = [] + for arg in self.args: + annotation = "" if arg.type is None else f": {arg.type}" + arg_strings.append(f"{arg.name}{annotation}") + def_base = "async def" if self.is_async else "def" + yield "{} {}({}) -> {}:".format( + def_base, self.name, ", ".join(arg_strings), self.return_type + ) + yield from self.body.generate() + + +class StaticMethod(Function): + def __init__( + self, name: str, args: list[TypedParam], body: Generable, return_type: str + ) -> None: + super().__init__(name, args, body, return_type, ("@staticmethod",)) + + +class ClassMethod(Function): + def __init__( + self, + name: str, + extra_args: list[TypedParam], + body: Generable, + return_type: str, + is_async: bool = False, + ) -> None: + args = [TypedParam("cls", None), *extra_args] + super().__init__(name, args, body, return_type, ("@classmethod",), is_async) + + +class Method(Function): + def __init__( + self, name: str, extra_args: list[TypedParam], body: Generable, return_type: str + ) -> None: + args = [TypedParam("self", None), *extra_args] + super().__init__(name, args, body, return_type) + + +class InitMethod(Method): + def __init__(self, extra_args: list[TypedParam], body: Generable) -> None: + super().__init__("__init__", extra_args, body, "None") + + +class Dataclass(Class): + def __init__( + self, + name, + attributes: list[TypingUnion[TypedParam, Assign, ClassMethod, Method]], + ) -> None: + super().__init__(name, None, attributes) + + def generate(self) -> Iterator[str]: + yield "@dataclass" + yield from super().generate() + + +class TypedDict(Class): + def __init__(self, name, params: list[TypedParam]) -> None: + super().__init__(name, ["typing.TypedDict"], params) + + def generate(self) -> Iterator[str]: + yield from super().generate() + + +class Try(Generable): + def __init__(self, try_body, to_catch, except_body): + if not isinstance(try_body, Suite): + try_body = Suite(try_body) + if not isinstance(except_body, Suite): + except_body = Suite(except_body) + self.try_body = try_body + self.to_catch = to_catch + self.except_body = except_body + + def generate(self): + yield "try:" + yield from self.try_body.generate() + yield f"except {self.to_catch}:" + yield from self.except_body.generate() + + +ANNOTATIONS_IMPORT = FromImport("__future__", ["annotations"]) diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/instructions.py b/basics/anchorpy-main/src/anchorpy/clientgen/instructions.py new file mode 100644 index 0000000..7c47eea --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/instructions.py @@ -0,0 +1,363 @@ +from pathlib import Path +from typing import Optional, Union, cast + +from anchorpy_core.idl import ( + Idl, + IdlAccountItem, + IdlAccounts, + IdlSeedConst, + IdlTypeArray, + IdlTypeSimple, +) +from autoflake import fix_code +from black import FileMode, format_str +from genpy import ( + Assign, + Collection, + FromImport, + If, + Import, + ImportAs, + Line, + Return, + Suite, +) +from pyheck import shouty_snake, snake, upper_camel + +from anchorpy.clientgen.common import ( + _field_to_encodable, + _layout_for_type, + _py_type_from_idl, + _sanitize, +) +from anchorpy.clientgen.genpy_extension import ( + ANNOTATIONS_IMPORT, + Call, + Function, + List, + NamedArg, + StrDict, + StrDictEntry, + TypedDict, + TypedParam, +) +from anchorpy.coder.common import _sighash +from anchorpy.coder.idl import FIELD_TYPE_MAP + +CONST_ACCOUNTS = { + "associated_token_program": "ASSOCIATED_TOKEN_PROGRAM_ID", + "rent": "RENT", + "system_program": "SYS_PROGRAM_ID", + "token_program": "TOKEN_PROGRAM_ID", + "clock": "CLOCK", +} + + +def gen_instructions(idl: Idl, root: Path, gen_pdas: bool) -> None: + instructions_dir = root / "instructions" + instructions_dir.mkdir(exist_ok=True) + gen_index_file(idl, instructions_dir) + instructions = gen_instructions_code(idl, instructions_dir, gen_pdas) + for path, code in instructions.items(): + formatted = format_str(code, mode=FileMode()) + fixed = fix_code(formatted, remove_all_unused_imports=True) + path.write_text(fixed) + + +def gen_index_file(idl: Idl, instructions_dir: Path) -> None: + code = gen_index_code(idl) + path = instructions_dir / "__init__.py" + formatted = format_str(code, mode=FileMode()) + path.write_text(formatted) + + +def gen_index_code(idl: Idl) -> str: + imports: list[FromImport] = [] + for ix in idl.instructions: + ix_name_snake_unsanitized = snake(ix.name) + ix_name = _sanitize(ix_name_snake_unsanitized) + import_members: list[str] = [ix_name] + if ix.args: + import_members.append(_args_interface_name(ix_name_snake_unsanitized)) + if ix.accounts: + import_members.append(_accounts_interface_name(ix_name_snake_unsanitized)) + if import_members: + imports.append(FromImport(f".{ix_name}", import_members)) + return str(Collection(imports)) + + +def _args_interface_name(ix_name: str) -> str: + return f"{upper_camel(ix_name)}Args" + + +def _accounts_interface_name(ix_name: str) -> str: + return f"{upper_camel(ix_name)}Accounts" + + +def recurse_accounts( + accs: list[IdlAccountItem], + nested_names: list[str], + const_accs: dict[int, str], + acc_idx: int = 0, +) -> tuple[list[str], int]: + elements: list[str] = [] + for acc in accs: + names = [*nested_names, _sanitize(snake(acc.name))] + if isinstance(acc, IdlAccounts): + nested_accs = cast(IdlAccounts, acc) + new_elements, acc_idx = recurse_accounts( + nested_accs.accounts, names, const_accs, acc_idx + ) + elements.extend(new_elements) + else: + acc_idx += 1 + try: + pubkey_var = const_accs[acc_idx] + except KeyError: + try: + pubkey_var = CONST_ACCOUNTS[names[-1]] + except KeyError: + nested_keys = [f'["{key}"]' for key in names] + dict_accessor = "".join(nested_keys) + pubkey_var = f"accounts{dict_accessor}" + if acc.is_optional: + elements.append( + f"AccountMeta(pubkey={pubkey_var}, " + f"is_signer={acc.is_signer}, " + f"is_writable={acc.is_mut}) " + f"if {pubkey_var} else " + f"AccountMeta(pubkey=program_id, " + f"is_signer=False, is_writable=False)" + ) + else: + elements.append( + f"AccountMeta(pubkey={pubkey_var}, " + f"is_signer={acc.is_signer}, " + f"is_writable={acc.is_mut})" + ) + return elements, acc_idx + + +def to_buffer_value( + ty: Union[IdlTypeSimple, IdlTypeArray], value: Union[str, int, list[int]] +) -> bytes: + if isinstance(value, int): + encoder = FIELD_TYPE_MAP[cast(IdlTypeSimple, ty)] + return encoder.build(value) + if isinstance(value, str): + return value.encode() + if isinstance(value, list): + return bytes(value) + raise ValueError(f"Unexpected type. ty: {ty}; value: {value}") + + +GenAccountsRes = tuple[list[TypedDict], list[Assign], dict[int, str], int] + + +def gen_accounts( + name, + idl_accs: list[IdlAccountItem], + gen_pdas: bool, + accum: Optional[GenAccountsRes] = None, +) -> GenAccountsRes: + if accum is None: + extra_typeddicts_to_use: list[TypedDict] = [] + accum_const_pdas: list[Assign] = [] + const_acc_indices: dict[int, str] = {} + acc_count = 0 + else: + extra_typeddicts_to_use, accum_const_pdas, const_acc_indices, acc_count = accum + params: list[TypedParam] = [] + const_pdas: list[Assign] = [] + for acc in idl_accs: + acc_name = _sanitize(snake(acc.name)) + if isinstance(acc, IdlAccounts): + nested_accs = cast(IdlAccounts, acc) + nested_acc_name = f"{upper_camel(nested_accs.name)}Nested" + nested_res = gen_accounts( + nested_acc_name, + nested_accs.accounts, + gen_pdas, + ( + extra_typeddicts_to_use, + accum_const_pdas, + const_acc_indices, + acc_count, + ), + ) + if nested_res[0]: + params.append(TypedParam(acc_name, f"{nested_acc_name}")) + extra_typeddicts_to_use = extra_typeddicts_to_use + nested_res[0] + accum_const_pdas = accum_const_pdas + nested_res[1] + const_acc_indices = const_acc_indices | nested_res[2] + acc_count = nested_res[3] + 1 + else: + acc_count += 1 + pda_generated = False + if gen_pdas: + maybe_pda = acc.pda + if maybe_pda is not None and all( + isinstance(seed, IdlSeedConst) for seed in maybe_pda.seeds + ): + seeds = cast(list[IdlSeedConst], maybe_pda.seeds) + const_pda_name = shouty_snake(f"{name}_{acc_name}") + const_pda_body_items = [ + str( + to_buffer_value( + cast(Union[IdlTypeSimple, IdlTypeArray], seed.ty), + cast(Union[str, int, list[int]], seed.value), + ) + ) + for seed in seeds + ] + seeds_arg = List(const_pda_body_items) + seeds_named_arg = NamedArg("seeds", seeds_arg) + const_pda_body = Call( + "Pubkey.find_program_address", + [seeds_named_arg, NamedArg("program_id", "PROGRAM_ID")], + ) + const_pdas.append(Assign(const_pda_name, f"{const_pda_body}[0]")) + const_acc_indices = { + **const_acc_indices, + acc_count: const_pda_name, + } + pda_generated = True + if not pda_generated: + try: + CONST_ACCOUNTS[acc_name] + except KeyError: + if acc.is_optional: + params.append(TypedParam(acc_name, "typing.Optional[Pubkey]")) + else: + params.append(TypedParam(acc_name, "Pubkey")) + maybe_typed_dict_container = [TypedDict(name, params)] if params else [] + accounts = maybe_typed_dict_container + extra_typeddicts_to_use + return accounts, accum_const_pdas + const_pdas, const_acc_indices, acc_count + + +def gen_instructions_code(idl: Idl, out: Path, gen_pdas: bool) -> dict[Path, str]: + types_import = [FromImport("..", ["types"])] if idl.types else [] + imports = [ + ANNOTATIONS_IMPORT, + Import("typing"), + FromImport("solders.pubkey", ["Pubkey"]), + FromImport("solders.system_program", ["ID as SYS_PROGRAM_ID"]), + FromImport("solders.sysvar", ["RENT", "CLOCK"]), + FromImport( + "spl.token.constants", ["TOKEN_PROGRAM_ID", "ASSOCIATED_TOKEN_PROGRAM_ID"] + ), + FromImport("solders.instruction", ["Instruction", "AccountMeta"]), + FromImport( + "anchorpy.borsh_extension", ["BorshPubkey", "EnumForCodegen", "COption"] + ), + FromImport("construct", ["Pass", "Construct"]), + ImportAs("borsh_construct", "borsh"), + *types_import, + FromImport("..program_id", ["PROGRAM_ID"]), + ] + result = {} + for ix in idl.instructions: + ix_name_snake_unsanitized = snake(ix.name) + ix_name = _sanitize(ix_name_snake_unsanitized) + filename = (out / ix_name).with_suffix(".py") + args_interface_params: list[TypedParam] = [] + layout_items: list[str] = [] + encoded_args_entries: list[StrDictEntry] = [] + accounts_interface_name = _accounts_interface_name(ix_name_snake_unsanitized) + for arg in ix.args: + arg_name = _sanitize(snake(arg.name)) + args_interface_params.append( + TypedParam( + arg_name, + _py_type_from_idl( + idl=idl, + ty=arg.ty, + types_relative_imports=False, + use_fields_interface_for_struct=False, + ), + ) + ) + layout_items.append( + _layout_for_type( + idl=idl, ty=arg.ty, name=arg_name, types_relative_imports=False + ) + ) + encoded_args_entries.append( + StrDictEntry( + arg_name, + _field_to_encodable( + idl=idl, + ty=arg, + types_relative_imports=False, + val_prefix='args["', + val_suffix='"]', + ), + ) + ) + if ix.args: + args_interface_name = _args_interface_name(ix_name) + args_interface_container = [ + TypedDict(args_interface_name, args_interface_params) + ] + layout_val = f"borsh.CStruct({','.join(layout_items)})" + layout_assignment_container = [Assign("layout", layout_val)] + args_container = [TypedParam("args", args_interface_name)] + encoded_args_val = f"layout.build({StrDict(encoded_args_entries)})" + else: + args_interface_container = [] + layout_val = "Pass" + args_container = [] + layout_assignment_container = [] + encoded_args_val = 'b""' + accounts_container = ( + [TypedParam("accounts", accounts_interface_name)] if ix.accounts else [] + ) + accounts, const_pdas, const_acc_indices, _ = gen_accounts( + accounts_interface_name, ix.accounts, gen_pdas + ) + recursed = recurse_accounts(ix.accounts, [], const_acc_indices)[0] + keys_assignment = Assign("keys: list[AccountMeta]", f"{List(recursed)}") + remaining_accounts_concatenation = If( + "remaining_accounts is not None", Line("keys += remaining_accounts") + ) + identifier_assignment = Assign( + "identifier", _sighash(ix_name_snake_unsanitized) + ) + encoded_args_assignment = Assign("encoded_args", encoded_args_val) + data_assignment = Assign("data", "identifier + encoded_args") + returning = Return("Instruction(program_id, data, keys)") + ix_fn = Function( + ix_name, + [ + *args_container, + *accounts_container, + TypedParam("program_id", "Pubkey = PROGRAM_ID"), + TypedParam( + "remaining_accounts", + "typing.Optional[typing.List[AccountMeta]] = None", + ), + ], + Suite( + [ + keys_assignment, + remaining_accounts_concatenation, + identifier_assignment, + encoded_args_assignment, + data_assignment, + returning, + ] + ), + "Instruction", + ) + contents = Collection( + [ + *imports, + *args_interface_container, + *layout_assignment_container, + *const_pdas, + *accounts, + ix_fn, + ] + ) + result[filename] = str(contents) + return result diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/program_id.py b/basics/anchorpy-main/src/anchorpy/clientgen/program_id.py new file mode 100644 index 0000000..770aafe --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/program_id.py @@ -0,0 +1,16 @@ +from pathlib import Path + +from black import FileMode, format_str +from genpy import Assign, Collection, FromImport + + +def gen_program_id_code(program_id: str) -> str: + import_line = FromImport("solders.pubkey", ["Pubkey"]) + assignment_line = Assign("PROGRAM_ID", f'Pubkey.from_string("{program_id}")') + return str(Collection([import_line, assignment_line])) + + +def gen_program_id(program_id: str, root: Path) -> None: + code = gen_program_id_code(program_id) + formatted = format_str(code, mode=FileMode()) + (root / "program_id.py").write_text(formatted) diff --git a/basics/anchorpy-main/src/anchorpy/clientgen/types.py b/basics/anchorpy-main/src/anchorpy/clientgen/types.py new file mode 100644 index 0000000..fa70033 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/clientgen/types.py @@ -0,0 +1,623 @@ +from dataclasses import dataclass +from pathlib import Path +from typing import Union as TypingUnion +from typing import cast + +from anchorpy_core.idl import ( + Idl, + IdlEnumVariant, + IdlField, + IdlType, + IdlTypeDefinitionTyStruct, +) +from autoflake import fix_code +from black import FileMode, format_str +from genpy import ( + Assign, + Collection, + FromImport, + Generable, + If, + Import, + ImportAs, + Raise, + Return, + Suite, +) +from pyheck import snake + +from anchorpy.clientgen.common import ( + _field_from_decoded, + _field_from_json, + _field_to_encodable, + _field_to_json, + _idl_type_to_json_type, + _json_interface_name, + _kind_interface_name, + _layout_for_type, + _py_type_from_idl, + _sanitize, + _value_interface_name, +) +from anchorpy.clientgen.genpy_extension import ( + ANNOTATIONS_IMPORT, + Call, + ClassMethod, + Dataclass, + Function, + Method, + NamedArg, + StrDict, + StrDictEntry, + Tuple, + TupleTypeAlias, + TypedDict, + TypedParam, + Union, +) + + +def gen_types(idl: Idl, root: Path) -> None: + types = idl.types + if types is None or not types: + return + types_dir = root / "types" + types_dir.mkdir(exist_ok=True) + gen_index_file(idl, types_dir) + gen_type_files(idl, types_dir) + + +def gen_index_file(idl: Idl, types_dir: Path) -> None: + code = gen_index_code(idl) + path = types_dir / "__init__.py" + formatted = format_str(code, mode=FileMode()) + path.write_text(formatted) + + +def gen_index_code(idl: Idl) -> str: + imports: list[TypingUnion[Import, FromImport]] = [Import("typing")] + for ty in idl.types: + ty_type = ty.ty + module_name = _sanitize(snake(ty.name)) + imports.append(FromImport(".", [module_name])) + import_members = ( + [_sanitize(ty.name), _json_interface_name(ty.name)] + if isinstance(ty_type, IdlTypeDefinitionTyStruct) + else [_kind_interface_name(ty.name), _json_interface_name(ty.name)] + ) + imports.append( + FromImport( + f".{module_name}", + import_members, + ) + ) + return str(Collection(imports)) + + +def gen_type_files(idl: Idl, types_dir: Path) -> None: + types_code = gen_types_code(idl, types_dir) + for path, code in types_code.items(): + formatted = format_str(code, mode=FileMode()) + fixed = fix_code(formatted, remove_all_unused_imports=True) + path.write_text(fixed) + + +def gen_types_code(idl: Idl, out: Path) -> dict[Path, str]: + res = {} + types_module_names = [_sanitize(snake(ty.name)) for ty in idl.types] + for ty in idl.types: + ty_name = _sanitize(ty.name) + module_name = _sanitize(snake(ty.name)) + relative_import_items = [ + mod for mod in types_module_names if mod != module_name + ] + relative_import_container = ( + [FromImport(".", relative_import_items)] if relative_import_items else [] + ) + ty_type = ty.ty + body = ( + gen_struct(idl, ty_name, ty_type.fields) + if isinstance(ty_type, IdlTypeDefinitionTyStruct) + else gen_enum(idl, ty_name, ty_type.variants) + ) + code = str(Collection([ANNOTATIONS_IMPORT, *relative_import_container, body])) + path = (out / module_name).with_suffix(".py") + res[path] = code + return res + + +def gen_struct(idl: Idl, name: str, fields: list[IdlField]) -> Collection: + imports = [ + Import("typing"), + FromImport("dataclasses", ["dataclass"]), + FromImport("construct", ["Container", "Construct"]), + FromImport("solders.pubkey", ["Pubkey"]), + FromImport("anchorpy.borsh_extension", ["BorshPubkey"]), + ImportAs("borsh_construct", "borsh"), + ] + json_interface_name = _json_interface_name(name) + field_params: list[TypedParam] = [] + json_interface_params: list[TypedParam] = [] + layout_items: list[str] = [] + from_decoded_items: list[str] = [] + to_encodable_items: list[str] = [] + to_json_items: list[str] = [] + from_json_items: list[str] = [] + for field in fields: + field_name = _sanitize(snake(field.name)) + field_params.append( + TypedParam( + field_name, + _py_type_from_idl( + idl=idl, + ty=field.ty, + types_relative_imports=True, + use_fields_interface_for_struct=False, + ), + ) + ) + json_interface_params.append( + TypedParam( + field_name, + _idl_type_to_json_type(ty=field.ty, types_relative_imports=True), + ) + ) + layout_items.append( + _layout_for_type( + idl=idl, ty=field.ty, name=field_name, types_relative_imports=True + ) + ) + from_decoded_item_val = _field_from_decoded( + idl=idl, + ty=IdlField(name=snake(field.name), docs=None, ty=field.ty), + val_prefix="obj.", + types_relative_imports=True, + ) + from_decoded_items.append(f"{field_name}={from_decoded_item_val}") + as_encodable = _field_to_encodable( + idl=idl, + ty=field, + val_prefix="self.", + val_suffix="", + types_relative_imports=True, + ) + to_encodable_items.append(f'"{field_name}": {as_encodable}') + to_json_items.append(f'"{field_name}": {_field_to_json(idl, field, "self.")}') + field_from_json = _field_from_json( + idl=idl, ty=field, types_relative_imports=True + ) + from_json_items.append(f"{field_name}={field_from_json}") + json_interface = TypedDict(json_interface_name, json_interface_params) + layout = f"borsh.CStruct({','.join(layout_items)})" + args_for_from_decoded = ",".join(from_decoded_items) + to_encodable_body = "{" + ",".join(to_encodable_items) + "}" + to_json_body = "{" + ",".join(to_json_items) + "}" + args_for_from_json = ",".join(from_json_items) + struct_cls = Dataclass( + name, + [ + Assign("layout: typing.ClassVar", layout), + *field_params, + ClassMethod( + "from_decoded", + [TypedParam("obj", "Container")], + Return(f"cls({args_for_from_decoded})"), + f'"{name}"', + ), + Method( + "to_encodable", + [], + Return(to_encodable_body), + "dict[str, typing.Any]", + ), + Method("to_json", [], Return(to_json_body), json_interface_name), + ClassMethod( + "from_json", + [TypedParam("obj", json_interface_name)], + Return(f"cls({args_for_from_json})"), + f'"{name}"', + ), + ], + ) + return Collection([*imports, json_interface, struct_cls]) + + +def _make_cstruct(fields: dict[str, str]) -> str: + formatted_fields = ",".join([f'"{key}" / {val}' for key, val in fields.items()]) + return f"borsh.CStruct({formatted_fields})" + + +@dataclass +class _NamedFieldRecord: + field_type_alias_entry: TypedParam + value_type_alias_entry: TypedParam + json_interface_value_type_entry: TypedParam + json_value_item: StrDictEntry + encodable_value_item: StrDictEntry + init_entry_for_from_decoded: NamedArg + init_entry_for_from_json: NamedArg + + +def _make_named_field_record( + named_field: IdlField, idl: Idl, cast_obj_var_name: str +) -> _NamedFieldRecord: + named_field_name = _sanitize(snake(named_field.name)) + return _NamedFieldRecord( + field_type_alias_entry=TypedParam( + named_field_name, + _py_type_from_idl( + idl=idl, + ty=named_field.ty, + types_relative_imports=True, + use_fields_interface_for_struct=False, + ), + ), + value_type_alias_entry=TypedParam( + named_field_name, + _py_type_from_idl( + idl=idl, + ty=named_field.ty, + types_relative_imports=True, + use_fields_interface_for_struct=False, + ), + ), + json_interface_value_type_entry=TypedParam( + named_field_name, + _idl_type_to_json_type(ty=named_field.ty, types_relative_imports=True), + ), + json_value_item=StrDictEntry( + named_field_name, + _field_to_json(idl, named_field, 'self.value["', val_suffix='"]'), + ), + encodable_value_item=StrDictEntry( + named_field_name, + _field_to_encodable( + idl=idl, + ty=named_field, + val_prefix='self.value["', + val_suffix='"]', + types_relative_imports=True, + ), + ), + init_entry_for_from_decoded=NamedArg( + named_field_name, + _field_from_decoded( + idl=idl, + ty=IdlField(f'val["{named_field_name}"]', docs=None, ty=named_field.ty), + types_relative_imports=True, + val_prefix="", + ), + ), + init_entry_for_from_json=NamedArg( + named_field_name, + _field_from_json( + idl=idl, + ty=named_field, + param_prefix=f'{cast_obj_var_name}["', + param_suffix='"]', + types_relative_imports=True, + ), + ), + ) + + +@dataclass +class _UnnamedFieldRecord: + field_type_alias_element: str + value_type_alias_element: str + json_interface_value_element: str + json_value_element: str + encodable_value_item: StrDictEntry + init_element_for_from_decoded: str + init_element_for_from_json: str + + +def _make_unnamed_field_record( + index: int, unnamed_field: IdlType, idl: Idl, cast_obj_var_name: str +) -> _UnnamedFieldRecord: + elem_name = f"value[{index}]" + encodable = _field_to_encodable( + idl=idl, + ty=IdlField(f"[{index}]", docs=None, ty=unnamed_field), + val_prefix="self.value", + types_relative_imports=True, + convert_case=False, + ) + return _UnnamedFieldRecord( + field_type_alias_element=_py_type_from_idl( + idl=idl, + ty=unnamed_field, + types_relative_imports=True, + use_fields_interface_for_struct=False, + ), + value_type_alias_element=_py_type_from_idl( + idl=idl, + ty=unnamed_field, + types_relative_imports=True, + use_fields_interface_for_struct=False, + ), + json_interface_value_element=_idl_type_to_json_type( + ty=unnamed_field, types_relative_imports=True + ), + json_value_element=_field_to_json( + idl, + IdlField(elem_name, docs=None, ty=unnamed_field), + "self.", + convert_case=False, + ), + encodable_value_item=StrDictEntry(f"item_{index}", encodable), + init_element_for_from_decoded=_field_from_decoded( + idl=idl, + ty=IdlField(f'val["item_{index}"]', docs=None, ty=unnamed_field), + val_prefix="", + types_relative_imports=True, + ), + init_element_for_from_json=_field_from_json( + idl=idl, + ty=IdlField(str(index), docs=None, ty=unnamed_field), + param_prefix=f"{cast_obj_var_name}[", + param_suffix="]", + types_relative_imports=True, + ), + ) + + +def gen_enum(idl: Idl, name: str, variants: list[IdlEnumVariant]) -> Collection: + imports = [ + Import("typing"), + FromImport("dataclasses", ["dataclass"]), + FromImport("solders.pubkey", ["Pubkey"]), + FromImport("construct", ["Construct"]), + FromImport("anchorpy.borsh_extension", ["EnumForCodegen", "BorshPubkey"]), + ImportAs("borsh_construct", "borsh"), + ] + invalid_enum_raise = Raise('ValueError("Invalid enum object")') + from_decoded_dict_check = If("not isinstance(obj, dict)", invalid_enum_raise) + variant_name_in_obj_checks: list[Generable] = [] + obj_kind_checks: list[Generable] = [] + json_interfaces: list[TypedDict] = [] + classes: list[Dataclass] = [] + cstructs: list[str] = [] + type_variants_members: list[str] = [] + json_variants_members: list[str] = [] + json_interface_value_field_types: list[TypingUnion[TypedDict, TupleTypeAlias]] = [] + value_type_aliases: list[TypingUnion[TypedDict, TupleTypeAlias]] = [] + for idx, variant in enumerate(variants): + discriminator = idx + fields = variant.fields + variant_name = _sanitize(variant.name) + value_interface_name = _value_interface_name(variant.name) + json_interface_name = _json_interface_name(variant.name) + json_interface_value_type_name = f"{json_interface_name}Value" + cast_obj_var_name = snake(json_interface_value_type_name) + encodable_value_items: list[StrDictEntry] = [] + cstruct_fields: dict[str, str] = {} + to_json_params_base = NamedArg("kind", f'"{variant.name}"') + json_interface_kind_field = TypedParam( + "kind", f'typing.Literal["{variant.name}"]' + ) + type_variants_members.append(variant_name) + json_variants_members.append(_json_interface_name(variant.name)) + + def make_variant_name_in_obj_check(then: Generable) -> Generable: + return If(f'"{variant.name}" in obj', then) + + def make_obj_kind_check(return_val: str) -> Generable: + accessed = 'obj["kind"]' + lhs = f"{accessed} == " + cast_obj_assignment_container = ( + [ + Assign( + cast_obj_var_name, + f'typing.cast({json_interface_value_type_name}, obj["value"])', + ) + ] + if fields + else [] + ) + return If( + f'{lhs}"{variant.name}"', + Suite([*cast_obj_assignment_container, Return(return_val)]), + ) + + if fields is not None: + flds = fields.fields + val_line_for_from_decoded = Assign("val", f'obj["{variant.name}"]') + if isinstance(flds[0], IdlField): + named_enum_fields = cast(list[IdlField], flds) + value_type_alias_entries: list[TypedParam] = [] + json_interface_value_type_entries: list[TypedParam] = [] + json_value_items: list[StrDictEntry] = [] + init_entries_for_from_decoded: list[NamedArg] = [] + init_entries_for_from_json: list[NamedArg] = [] + for named_field in named_enum_fields: + rec = _make_named_field_record(named_field, idl, cast_obj_var_name) + value_type_alias_entries.append(rec.value_type_alias_entry) + json_interface_value_type_entries.append( + rec.json_interface_value_type_entry + ) + json_value_items.append(rec.json_value_item) + encodable_value_items.append(rec.encodable_value_item) + init_entries_for_from_decoded.append( + rec.init_entry_for_from_decoded + ) + init_entries_for_from_json.append(rec.init_entry_for_from_json) + + cstruct_fields[snake(named_field.name)] = _layout_for_type( + idl=idl, ty=named_field.ty, types_relative_imports=True + ) + value_type_aliases.append( + TypedDict(value_interface_name, value_type_alias_entries) + ) + json_interface_value_field_type = TypedDict( + json_interface_value_type_name, json_interface_value_type_entries + ) + json_value_entry = StrDict(json_value_items) + to_json_body = Return( + Call( + json_interface_name, + [to_json_params_base, NamedArg("value", str(json_value_entry))], + ) + ) + init_arg_for_from_decoded = Call( + value_interface_name, init_entries_for_from_decoded + ) + init_arg_for_from_json = Call( + value_interface_name, init_entries_for_from_json + ) + else: + tuple_enum_fields = cast(list[IdlType], flds) + field_type_alias_elements: list[str] = [] + value_type_alias_elements: list[str] = [] + json_interface_value_elements: list[str] = [] + json_value_elements: list[str] = [] + init_elements_for_from_decoded: list[str] = [] + init_elements_for_from_json: list[str] = [] + for i, unnamed_field in enumerate(tuple_enum_fields): + rec_unnamed = _make_unnamed_field_record( + i, unnamed_field, idl, cast_obj_var_name + ) + field_type_alias_elements.append( + rec_unnamed.field_type_alias_element + ) + value_type_alias_elements.append( + rec_unnamed.value_type_alias_element + ) + json_interface_value_elements.append( + rec_unnamed.json_interface_value_element + ) + json_value_elements.append(rec_unnamed.json_value_element) + encodable_value_items.append(rec_unnamed.encodable_value_item) + init_elements_for_from_decoded.append( + rec_unnamed.init_element_for_from_decoded + ) + init_elements_for_from_json.append( + rec_unnamed.init_element_for_from_json + ) + cstruct_fields[f"item_{i}"] = _layout_for_type( + idl=idl, ty=unnamed_field, types_relative_imports=True + ) + value_type_aliases.append( + TupleTypeAlias( + value_interface_name, + value_type_alias_elements, + ) + ) + json_interface_value_field_type = TupleTypeAlias( + json_interface_value_type_name, json_interface_value_elements + ) + to_json_body = Return( + Call( + json_interface_name, + [ + to_json_params_base, + NamedArg("value", str(Tuple(json_value_elements))), + ], + ) + ) + init_arg_for_from_decoded = Tuple(init_elements_for_from_decoded) + init_arg_for_from_json = Tuple(init_elements_for_from_json) + value_field_container = [TypedParam("value", value_interface_name)] + to_json_method = Method("to_json", [], to_json_body, json_interface_name) + encodable_value_entry = StrDict(encodable_value_items) + to_encodable_body = Return( + StrDict([StrDictEntry(variant.name, encodable_value_entry)]) + ) + to_encodable_method = Method("to_encodable", [], to_encodable_body, "dict") + json_interface_params = [ + TypedParam("value", json_interface_value_type_name), + json_interface_kind_field, + ] + json_interface_value_field_types.append(json_interface_value_field_type) + variant_name_in_obj_check = make_variant_name_in_obj_check( + Suite( + [ + val_line_for_from_decoded, + Return( + f"{_sanitize(variant.name)}" + f"({init_arg_for_from_decoded})" + ), + ] + ) + ) + obj_kind_check = make_obj_kind_check( + f"{_sanitize(variant.name)}({init_arg_for_from_json})", + ) + else: + to_json_method = ClassMethod( + "to_json", + [], + Return(Call(json_interface_name, [to_json_params_base])), + json_interface_name, + ) + to_encodable_method = ClassMethod( + "to_encodable", + [], + Return(StrDict([StrDictEntry(variant.name, StrDict([]))])), + "dict", + ) + value_field_container = [] + json_interface_params = [json_interface_kind_field] + variant_name_in_obj_check = make_variant_name_in_obj_check( + Return(f"{_sanitize(variant.name)}()") + ) + obj_kind_check = make_obj_kind_check(f"{_sanitize(variant.name)}()") + json_interfaces.append(TypedDict(json_interface_name, json_interface_params)) + class_common_attrs = [ + Assign("discriminator: typing.ClassVar", discriminator), + Assign("kind: typing.ClassVar", f'"{variant.name}"'), + ] + + attrs = [ + *class_common_attrs, + *value_field_container, + to_json_method, + to_encodable_method, + ] + classes.append(Dataclass(_sanitize(variant.name), attrs)) + variant_name_in_obj_checks.append(variant_name_in_obj_check) + obj_kind_checks.append(obj_kind_check) + cstructs.append(f'"{variant.name}" / {_make_cstruct(cstruct_fields)}') + from_decoded_fn = Function( + "from_decoded", + [TypedParam("obj", "dict")], + Suite( + [from_decoded_dict_check, *variant_name_in_obj_checks, invalid_enum_raise] + ), + _kind_interface_name(name), + ) + from_json_fn = Function( + "from_json", + [TypedParam("obj", _json_interface_name(name))], + Suite( + [ + *obj_kind_checks, + Assign("kind", 'obj["kind"]'), + Raise('ValueError(f"Unrecognized enum kind: {kind}")'), + ] + ), + _kind_interface_name(name), + ) + formatted_cstructs = ",".join(cstructs) + layout_assignment = Assign( + "layout", + f"EnumForCodegen({formatted_cstructs})", + ) + json_variants = Union(json_variants_members) + type_variants = Union(type_variants_members) + kind_type_alias = Assign(_kind_interface_name(name), type_variants) + json_type_alias = Assign(_json_interface_name(name), json_variants) + return Collection( + [ + *imports, + *json_interface_value_field_types, + *value_type_aliases, + *json_interfaces, + *classes, + kind_type_alias, + json_type_alias, + from_decoded_fn, + from_json_fn, + layout_assignment, + ] + ) diff --git a/basics/anchorpy-main/src/anchorpy/coder/__init__.py b/basics/anchorpy-main/src/anchorpy/coder/__init__.py new file mode 100644 index 0000000..1bcf95b --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/__init__.py @@ -0,0 +1 @@ +"""This subpackage defines the Coder class.""" diff --git a/basics/anchorpy-main/src/anchorpy/coder/accounts.py b/basics/anchorpy-main/src/anchorpy/coder/accounts.py new file mode 100644 index 0000000..af2f999 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/accounts.py @@ -0,0 +1,73 @@ +"""This module provides `AccountsCoder` and `_account_discriminator`.""" +from hashlib import sha256 +from typing import Any, Tuple + +from anchorpy_core.idl import Idl +from construct import Adapter, Bytes, Container, Sequence, Switch + +from anchorpy.coder.idl import _typedef_layout +from anchorpy.program.common import NamedInstruction as AccountToSerialize + +ACCOUNT_DISCRIMINATOR_SIZE = 8 # bytes + + +class AccountsCoder(Adapter): + """Encodes and decodes account data.""" + + def __init__(self, idl: Idl) -> None: + """Init. + + Args: + idl: The parsed IDL object. + """ + self._accounts_layout = { + acc.name: _typedef_layout(acc, idl.types, acc.name) for acc in idl.accounts + } + self.acc_name_to_discriminator = { + acc.name: _account_discriminator(acc.name) for acc in idl.accounts + } + self.discriminator_to_acc_name = { + disc: acc_name for acc_name, disc in self.acc_name_to_discriminator.items() + } + discriminator_to_typedef_layout = { + disc: self._accounts_layout[acc_name] + for acc_name, disc in self.acc_name_to_discriminator.items() + } + subcon = Sequence( + "discriminator" / Bytes(ACCOUNT_DISCRIMINATOR_SIZE), + Switch(lambda this: this.discriminator, discriminator_to_typedef_layout), + ) + super().__init__(subcon) # type: ignore + + def decode(self, obj: bytes) -> Container[Any]: + """Decode account data. + + Args: + obj: Data to decode. + + Returns: + Decoded data. + """ + return self.parse(obj).data + + def _decode(self, obj: Tuple[bytes, Any], context, path) -> AccountToSerialize: + return AccountToSerialize( + data=obj[1], + name=self.discriminator_to_acc_name[obj[0]], + ) + + def _encode(self, obj: AccountToSerialize, context, path) -> Tuple[bytes, Any]: + discriminator = self.acc_name_to_discriminator[obj.name] + return discriminator, obj.data + + +def _account_discriminator(name: str) -> bytes: + """Calculate unique 8 byte discriminator prepended to all anchor accounts. + + Args: + name: The account name. + + Returns: + The discriminator in bytes. + """ + return sha256(f"account:{name}".encode()).digest()[:ACCOUNT_DISCRIMINATOR_SIZE] diff --git a/basics/anchorpy-main/src/anchorpy/coder/coder.py b/basics/anchorpy-main/src/anchorpy/coder/coder.py new file mode 100644 index 0000000..72cfb62 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/coder.py @@ -0,0 +1,20 @@ +"""Provides the Coder class.""" +from anchorpy_core.idl import Idl + +from anchorpy.coder.accounts import AccountsCoder +from anchorpy.coder.event import EventCoder +from anchorpy.coder.instruction import InstructionCoder + + +class Coder: + """Coder provides a facade for encoding and decoding all IDL related objects.""" + + def __init__(self, idl: Idl): + """Initialize the coder. + + Args: + idl: a parsed Idl instance. + """ + self.instruction: InstructionCoder = InstructionCoder(idl) + self.accounts: AccountsCoder = AccountsCoder(idl) + self.events: EventCoder = EventCoder(idl) diff --git a/basics/anchorpy-main/src/anchorpy/coder/common.py b/basics/anchorpy-main/src/anchorpy/coder/common.py new file mode 100644 index 0000000..8a8872b --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/common.py @@ -0,0 +1,125 @@ +"""Common utilities for encoding and decoding.""" +from hashlib import sha256 +from typing import Dict, Union + +from anchorpy_core.idl import ( + Idl, + IdlEnumVariant, + IdlField, + IdlType, + IdlTypeArray, + IdlTypeCompound, + IdlTypeDefined, + IdlTypeDefinition, + IdlTypeDefinitionTyEnum, + IdlTypeOption, + IdlTypeSimple, + IdlTypeVec, +) + + +def _sighash(ix_name: str) -> bytes: + """Not technically sighash, since we don't include the arguments. + + (Because Rust doesn't allow function overloading.) + + Args: + ix_name: The instruction name. + + Returns: + The sighash bytes. + """ + formatted_str = f"global:{ix_name}" + return sha256(formatted_str.encode()).digest()[:8] + + +def _type_size_compound_type(idl: Idl, ty: IdlTypeCompound) -> int: + if isinstance(ty, IdlTypeVec): + return 1 + if isinstance(ty, IdlTypeOption): + return 1 + _type_size(idl, ty.option) + if isinstance(ty, IdlTypeDefined): + defined = ty.defined + filtered = [t for t in idl.types if t.name == defined] + if len(filtered) != 1: + raise ValueError(f"Type not found {ty}") + type_def = filtered[0] + return _account_size(idl, type_def) + if isinstance(ty, IdlTypeArray): + element_type = ty.array[0] + array_size = ty.array[1] + return _type_size(idl, element_type) * array_size + raise ValueError(f"type_size not implemented for {ty}") + + +def _type_size(idl: Idl, ty: IdlType) -> int: + """Return the size of the type in bytes. + + For variable length types, just return 1. + Users should override this value in such cases. + + Args: + idl: The parsed `Idl` object. + ty: The type object from the IDL. + + Returns: + The size of the object in bytes. + """ + sizes: Dict[IdlTypeSimple, int] = { + IdlTypeSimple.Bool: 1, + IdlTypeSimple.U8: 1, + IdlTypeSimple.I8: 1, + IdlTypeSimple.Bytes: 1, + IdlTypeSimple.String: 1, + IdlTypeSimple.I16: 2, + IdlTypeSimple.U16: 2, + IdlTypeSimple.U32: 4, + IdlTypeSimple.I32: 4, + IdlTypeSimple.F32: 4, + IdlTypeSimple.U64: 8, + IdlTypeSimple.I64: 8, + IdlTypeSimple.F64: 8, + IdlTypeSimple.U128: 16, + IdlTypeSimple.I128: 16, + IdlTypeSimple.PublicKey: 32, + } + if isinstance(ty, IdlTypeSimple): + return sizes[ty] + return _type_size_compound_type(idl, ty) + + +def _variant_field_size(idl: Idl, field: Union[IdlField, IdlType]) -> int: + if isinstance(field, IdlField): + return _type_size(idl, field.ty) + return _type_size(idl, field) + + +def _variant_size(idl: Idl, variant: IdlEnumVariant) -> int: + if variant.fields is None: + return 0 + field_sizes = [] + field: Union[IdlField, IdlType] + for field in variant.fields.fields: + field_sizes.append(_variant_field_size(idl, field)) + return sum(field_sizes) + + +def _account_size(idl: Idl, idl_account: IdlTypeDefinition) -> int: + """Calculate account size in bytes. + + Args: + idl: The parsed `Idl` instance. + idl_account: An item from `idl.accounts`. + + Returns: + Account size. + """ + idl_account_type = idl_account.ty + if isinstance(idl_account_type, IdlTypeDefinitionTyEnum): + variant_sizes = ( + _variant_size(idl, variant) for variant in idl_account_type.variants + ) + return max(variant_sizes) + 1 + if idl_account_type.fields is None: + return 0 + return sum(_type_size(idl, f.ty) for f in idl_account_type.fields) diff --git a/basics/anchorpy-main/src/anchorpy/coder/event.py b/basics/anchorpy-main/src/anchorpy/coder/event.py new file mode 100644 index 0000000..5abd0a2 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/event.py @@ -0,0 +1,82 @@ +"""This module deals with (de)serializing Anchor events.""" +from hashlib import sha256 +from typing import Any, Dict, Optional, Tuple + +from anchorpy_core.idl import ( + Idl, + IdlEvent, + IdlField, + IdlTypeDefinition, + IdlTypeDefinitionTyStruct, +) +from construct import Adapter, Bytes, Construct, Sequence, Switch +from pyheck import snake + +from anchorpy.coder.idl import _typedef_layout +from anchorpy.program.common import Event + + +def _event_discriminator(name: str) -> bytes: + """Get 8-byte discriminator from event name. + + Args: + name: The event name. + + Returns: + Discriminator + """ + return sha256(f"event:{name}".encode()).digest()[:8] + + +def _event_layout(event: IdlEvent, idl: Idl) -> Construct: + event_type_def = IdlTypeDefinition( + name=event.name, + docs=None, + ty=IdlTypeDefinitionTyStruct( + fields=[ + IdlField(name=snake(f.name), docs=None, ty=f.ty) for f in event.fields + ], + ), + ) + return _typedef_layout(event_type_def, idl.types, event.name) + + +class EventCoder(Adapter): + """Encodes and decodes Anchor events.""" + + def __init__(self, idl: Idl): + """Initialize the EventCoder. + + Args: + idl: The parsed Idl object. + """ + self.idl = idl + idl_events = idl.events + layouts: Dict[str, Construct] + if idl_events: + layouts = {event.name: _event_layout(event, idl) for event in idl_events} + else: + layouts = {} + self.layouts = layouts + self.discriminators: Dict[bytes, str] = ( + {} + if idl_events is None + else {_event_discriminator(event.name): event.name for event in idl_events} + ) + self.discriminator_to_layout = { + disc: self.layouts[event_name] + for disc, event_name in self.discriminators.items() + } + subcon = Sequence( + "discriminator" / Bytes(8), # not base64-encoded here + Switch(lambda this: this.discriminator, self.discriminator_to_layout), + ) + super().__init__(subcon) # type: ignore + + def _decode(self, obj: Tuple[bytes, Any], context, path) -> Optional[Event]: + disc = obj[0] + try: + event_name = self.discriminators[disc] + except KeyError: + return None + return Event(data=obj[1], name=event_name) diff --git a/basics/anchorpy-main/src/anchorpy/coder/idl.py b/basics/anchorpy-main/src/anchorpy/coder/idl.py new file mode 100644 index 0000000..5651ae3 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/idl.py @@ -0,0 +1,325 @@ +"""IDL coding.""" +from dataclasses import fields as dc_fields +from dataclasses import make_dataclass +from keyword import kwlist +from types import MappingProxyType +from typing import Mapping, Type, cast + +from anchorpy_core.idl import ( + IdlField, + IdlType, + IdlTypeArray, + IdlTypeDefined, + IdlTypeDefinition, + IdlTypeDefinitionTyEnum, + IdlTypeDefinitionTyStruct, + IdlTypeOption, + IdlTypeSimple, + IdlTypeVec, +) +from borsh_construct import ( + F32, + F64, + I8, + I16, + I32, + I64, + I128, + U8, + U16, + U32, + U64, + U128, + Bool, + Bytes, + CStruct, + Enum, + Option, + String, + TupleStruct, + Vec, +) +from construct import Construct +from pyheck import snake + +from anchorpy.borsh_extension import BorshPubkey, _DataclassStruct +from anchorpy.idl import TypeDefs + +FIELD_TYPE_MAP: Mapping[IdlTypeSimple, Construct] = MappingProxyType( + { + IdlTypeSimple.Bool: Bool, + IdlTypeSimple.U8: U8, + IdlTypeSimple.I8: I8, + IdlTypeSimple.U16: U16, + IdlTypeSimple.I16: I16, + IdlTypeSimple.U32: U32, + IdlTypeSimple.I32: I32, + IdlTypeSimple.F32: F32, + IdlTypeSimple.U64: U64, + IdlTypeSimple.I64: I64, + IdlTypeSimple.F64: F64, + IdlTypeSimple.U128: U128, + IdlTypeSimple.I128: I128, + IdlTypeSimple.Bytes: Bytes, + IdlTypeSimple.String: String, + IdlTypeSimple.PublicKey: BorshPubkey, + }, +) + + +_enums_cache: dict[tuple[str, str], Enum] = {} + + +def _handle_enum_variants( + idl_enum: IdlTypeDefinitionTyEnum, + types: TypeDefs, + name: str, +) -> Enum: + dict_key = (name, str(idl_enum)) + try: + return _enums_cache[dict_key] + except KeyError: + result = _handle_enum_variants_no_cache(idl_enum, types, name) + _enums_cache[dict_key] = result + return result + + +def _handle_enum_variants_no_cache( + idl_enum: IdlTypeDefinitionTyEnum, + types: TypeDefs, + name: str, +) -> Enum: + variants = [] + dclasses = {} + for variant in idl_enum.variants: + variant_name = variant.name + if variant.fields is None: + variants.append(variant_name) + else: + variant_fields = variant.fields + flds = variant_fields.fields + if isinstance(flds[0], IdlField): + fields = [] + named_fields = cast(list[IdlField], flds) + for fld in named_fields: + fields.append(_field_layout(fld, types)) + cstruct = CStruct(*fields) + datacls = _idl_enum_fields_named_to_dataclass_type( + named_fields, + variant_name, + ) + dclasses[variant_name] = datacls + renamed = variant_name / cstruct + else: + fields = [] + unnamed_fields = cast(list[IdlType], flds) + for type_ in unnamed_fields: + fields.append(_type_layout(type_, types)) + tuple_struct = TupleStruct(*fields) + renamed = variant_name / tuple_struct + variants.append(renamed) # type: ignore + enum_without_types = Enum(*variants, enum_name=name) + if dclasses: + for cname in enum_without_types.enum._sumtype_constructor_names: + try: + dclass = dclasses[cname] + except KeyError: + continue + dclass_fields = dc_fields(dclass) + constructr = getattr(enum_without_types.enum, cname) + for constructor_field in constructr._sumtype_attribs: + attrib = constructor_field[1] # type: ignore + fld_name = constructor_field[0] # type: ignore + dclass_field = [f for f in dclass_fields if f.name == fld_name][0] + attrib.type = dclass_field.type # type: ignore + return enum_without_types + + +def _typedef_layout_without_field_name( + typedef: IdlTypeDefinition, + types: TypeDefs, +) -> Construct: + typedef_type = typedef.ty + name = typedef.name + if isinstance(typedef_type, IdlTypeDefinitionTyStruct): + field_layouts = [_field_layout(field, types) for field in typedef_type.fields] + cstruct = CStruct(*field_layouts) + datacls = _idl_typedef_ty_struct_to_dataclass_type(typedef_type, name) + return _DataclassStruct(cstruct, datacls=datacls) + elif isinstance(typedef_type, IdlTypeDefinitionTyEnum): + return _handle_enum_variants(typedef_type, types, name) + unknown_type = typedef_type.kind + raise ValueError(f"Unknown type {unknown_type}") + + +def _typedef_layout( + typedef: IdlTypeDefinition, + types: list[IdlTypeDefinition], + field_name: str, +) -> Construct: + """Map an IDL typedef to a `Construct` object. + + Args: + typedef: The IDL typedef object. + types: IDL type definitions. + field_name: The name of the field. + + Raises: + ValueError: If an unknown type is passed. + + Returns: + `Construct` object from `borsh-construct`. + """ + return field_name / _typedef_layout_without_field_name(typedef, types) + + +def _type_layout(type_: IdlType, types: TypeDefs) -> Construct: + if isinstance(type_, IdlTypeSimple): + return FIELD_TYPE_MAP[type_] + if isinstance(type_, IdlTypeVec): + return Vec(_type_layout(type_.vec, types)) + elif isinstance(type_, IdlTypeOption): + return Option(_type_layout(type_.option, types)) + elif isinstance(type_, IdlTypeDefined): + defined = type_.defined + if not types: + raise ValueError("User defined types not provided") + filtered = [t for t in types if t.name == defined] + if len(filtered) != 1: + raise ValueError(f"Type not found {defined}") + return _typedef_layout_without_field_name(filtered[0], types) + elif isinstance(type_, IdlTypeArray): + array_ty = type_.array[0] + array_len = type_.array[1] + inner_layout = _type_layout(array_ty, types) + return inner_layout[array_len] + raise ValueError(f"Type {type_} not implemented yet") + + +def _field_layout(field: IdlField, types: TypeDefs) -> Construct: + """Map IDL spec to `borsh-construct` types. + + Args: + field: field object from the IDL. + types: IDL type definitions. + + Raises: + ValueError: If the user-defined types are not provided. + ValueError: If the type is not found. + ValueError: If the type is not implemented yet. + + Returns: + `Construct` object from `borsh-construct`. + """ + field_name = snake(field.name) if field.name else "" + return field_name / _type_layout(field.ty, types) + + +def _make_datacls(name: str, fields: list[str]) -> type: + return make_dataclass(name, fields) + + +_idl_typedef_ty_struct_to_dataclass_type_cache: dict[tuple[str, str], Type] = {} + + +def _idl_typedef_ty_struct_to_dataclass_type( + typedef_type: IdlTypeDefinitionTyStruct, + name: str, +) -> Type: + dict_key = (name, str(typedef_type)) + try: + return _idl_typedef_ty_struct_to_dataclass_type_cache[dict_key] + except KeyError: + result = _idl_typedef_ty_struct_to_dataclass_type_no_cache(typedef_type, name) + _idl_typedef_ty_struct_to_dataclass_type_cache[dict_key] = result + return result + + +def _idl_typedef_ty_struct_to_dataclass_type_no_cache( + typedef_type: IdlTypeDefinitionTyStruct, + name: str, +) -> Type: + """Generate a dataclass definition from an IDL struct. + + Args: + typedef_type: The IDL type. + name: The name of the dataclass. + + Returns: + Dataclass definition. + """ + dataclass_fields = [] + for field in typedef_type.fields: + field_name = snake(field.name) + field_name_to_use = f"{field_name}_" if field_name in kwlist else field_name + dataclass_fields.append( + field_name_to_use, + ) + return _make_datacls(name, dataclass_fields) + + +_idl_enum_fields_named_to_dataclass_type_cache: dict[tuple[str, str], Type] = {} + + +def _idl_enum_fields_named_to_dataclass_type( + fields: list[IdlField], + name: str, +) -> Type: + dict_key = (name, str(fields)) + try: + return _idl_enum_fields_named_to_dataclass_type_cache[dict_key] + except KeyError: + result = _idl_enum_fields_named_to_dataclass_type_no_cache(fields, name) + _idl_enum_fields_named_to_dataclass_type_cache[dict_key] = result + return result + + +def _idl_enum_fields_named_to_dataclass_type_no_cache( + fields: list[IdlField], + name: str, +) -> Type: + """Generate a dataclass definition from IDL named enum fields. + + Args: + fields: The IDL enum fields. + name: The name of the dataclass. + + Returns: + Dataclass type definition. + """ + dataclass_fields = [] + for field in fields: + field_name = snake(field.name) + field_name_to_use = f"{field_name}_" if field_name in kwlist else field_name + dataclass_fields.append( + field_name_to_use, + ) + return _make_datacls(name, dataclass_fields) + + +def _idl_typedef_to_python_type( + typedef: IdlTypeDefinition, + types: TypeDefs, +) -> Type: + """Generate Python type from IDL user-defined type. + + Args: + typedef: The user-defined type. + types: IDL type definitions. + + Raises: + ValueError: If an unknown type is passed. + + Returns: + The Python type. + """ + typedef_type = typedef.ty + if isinstance(typedef_type, IdlTypeDefinitionTyStruct): + return _idl_typedef_ty_struct_to_dataclass_type( + typedef_type, + typedef.name, + ) + elif isinstance(typedef_type, IdlTypeDefinitionTyEnum): + return _handle_enum_variants(typedef_type, types, typedef.name).enum + unknown_type = typedef_type.kind + raise ValueError(f"Unknown type {unknown_type}") diff --git a/basics/anchorpy-main/src/anchorpy/coder/instruction.py b/basics/anchorpy-main/src/anchorpy/coder/instruction.py new file mode 100644 index 0000000..e648f44 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/coder/instruction.py @@ -0,0 +1,98 @@ +"""This module deals (de)serializing program instructions.""" +from typing import Any, Dict, Protocol, Tuple, TypeVar, cast + +from anchorpy_core.idl import Idl +from borsh_construct import CStruct +from construct import Adapter, Bytes, Construct, Container, Sequence, Switch +from pyheck import snake + +from anchorpy.coder.common import _sighash +from anchorpy.coder.idl import _field_layout +from anchorpy.idl import TypeDefs +from anchorpy.program.common import NamedInstruction + + +class _Sighash(Adapter): + """Sighash as a Construct Adapter.""" + + def __init__(self) -> None: + """Initialize.""" + super().__init__(Bytes(8)) # type: ignore + + def _encode(self, obj: str, context, path) -> bytes: + return _sighash(obj) + + def _decode(self, obj: bytes, context, path): + raise ValueError("Sighash cannot be reversed") + + +class InstructionCoder(Adapter): + """Encodes and decodes program instructions.""" + + def __init__(self, idl: Idl) -> None: + """Init. + + Args: + idl: The parsed IDL object. + """ + self.ix_layout = _parse_ix_layout(idl) + sighasher = _Sighash() + sighash_layouts: Dict[bytes, Construct] = {} + sighashes: Dict[str, bytes] = {} + sighash_to_name: Dict[bytes, str] = {} + for ix in idl.instructions: + ix_name = snake(ix.name) + sh = sighasher.build(ix_name) + sighashes[ix_name] = sh + sighash_layouts[sh] = self.ix_layout[ix_name] + sighash_to_name[sh] = ix_name + self.sighash_layouts = sighash_layouts + self.sighashes = sighashes + self.sighash_to_name = sighash_to_name + subcon = Sequence( + "sighash" / Bytes(8), + Switch(lambda this: this.sighash, sighash_layouts), + ) + super().__init__(subcon) # type: ignore + + def encode(self, ix_name: str, ix: Dict[str, Any]) -> bytes: + """Encode a program instruction. + + Args: + ix_name: The name of the instruction + ix: The data to encode. + + Returns: + The encoded instruction. + """ + return self.build(NamedInstruction(name=ix_name, data=ix)) + + def _decode(self, obj: Tuple[bytes, Any], context, path) -> NamedInstruction: + return NamedInstruction(data=obj[1], name=self.sighash_to_name[obj[0]]) + + def _encode( + self, obj: NamedInstruction, context: Container, path + ) -> Tuple[bytes, Any]: + return (self.sighashes[obj.name], obj.data) + + +_SA = TypeVar("_SA", bound="_SupportsAdd") + + +class _SupportsAdd(Protocol): + """Any type T where +(:T, :T) -> T.""" + + def __add__(self: _SA, other: _SA) -> _SA: + ... + + +def _parse_ix_layout(idl: Idl) -> Dict[str, Construct]: + ix_layout: Dict[str, Construct] = {} + for ix in idl.instructions: + typedefs = cast(_SupportsAdd, idl.accounts) + cast(_SupportsAdd, idl.types) + field_layouts = [ + _field_layout(arg, cast(TypeDefs, typedefs)) for arg in ix.args + ] + ix_name = snake(ix.name) + ix_layout[ix_name] = ix_name / CStruct(*field_layouts) + return ix_layout diff --git a/basics/anchorpy-main/src/anchorpy/error.py b/basics/anchorpy-main/src/anchorpy/error.py new file mode 100644 index 0000000..ef7aa6b --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/error.py @@ -0,0 +1,340 @@ +"""This module handles AnchorPy errors.""" +from __future__ import annotations + +import re +from enum import IntEnum +from typing import Dict, List, Optional, Tuple + +from solders.pubkey import Pubkey +from solders.rpc.errors import SendTransactionPreflightFailureMessage +from solders.rpc.responses import RPCError +from solders.transaction_status import ( + InstructionErrorCustom, + TransactionErrorInstructionError, + TransactionErrorType, +) + + +class AccountDoesNotExistError(Exception): + """Raise if account doesn't exist.""" + + +class AccountInvalidDiscriminator(Exception): + """Raise if account discriminator doesn't match the IDL.""" + + +class IdlNotFoundError(Exception): + """Raise when requested IDL account does not exist.""" + + +class ArgsError(Exception): + """Raise when the incorrect number of args is passed to the RPC function.""" + + +class _LangErrorCode(IntEnum): + """Enumerates Anchor error codes.""" + + # Instructions. + InstructionMissing = 100 + InstructionFallbackNotFound = 101 + InstructionDidNotDeserialize = 102 + InstructionDidNotSerialize = 103 + # IDL instructions. + IdlInstructionStub = 1000 + IdlInstructionInvalidProgram = 1001 + # Constraints. + ConstraintMut = 2000 + ConstraintHasOne = 2001 + ConstraintSigner = 2002 + ConstraintRaw = 2003 + ConstraintOwner = 2004 + ConstraintRentExempt = 2005 + ConstraintSeeds = 2006 + ConstraintExecutable = 2007 + ConstraintState = 2008 + ConstraintAssociated = 2009 + ConstraintAssociatedInit = 2010 + ConstraintClose = 2011 + ConstraintAddress = 2012 + ConstraintZero = 2013 + ConstraintTokenMint = 2014 + ConstraintTokenOwner = 2015 + ConstraintMintMintAuthority = 2016 + ConstraintMintFreezeAuthority = 2017 + ConstraintMintDecimals = 2018 + ConstraintSpace = 2019 + # Require + RequireViolated = 2500 + RequireEqViolated = 2501 + RequireKeysEqViolated = 2502 + RequireNeqViolated = 2503 + RequireKeysNeqViolated = 2504 + RequireGtViolated = 2505 + RequireGteViolated = 2506 + # Accounts. + AccountDiscriminatorAlreadySet = 3000 + AccountDiscriminatorNotFound = 3001 + AccountDiscriminatorMismatch = 3002 + AccountDidNotDeserialize = 3003 + AccountDidNotSerialize = 3004 + AccountNotEnoughKeys = 3005 + AccountNotMutable = 3006 + AccountOwnedByWrongProgram = 3007 + InvalidProgramId = 3008 + InvalidProgramExecutable = 3009 + AccountNotSigner = 3010 + AccountNotSystemOwned = 3011 + AccountNotInitialized = 3012 + AccountNotProgramData = 3013 + AccountNotAssociatedTokenAccount = 3014 + AccountSysvarMismatch = 3015 + # State. + StateInvalidAddress = 4000 + + # Used for APIs that shouldn't be used anymore. + Deprecated = 5000 + + +LangErrorMessage: Dict[int, str] = { + # Instructions. + _LangErrorCode.InstructionMissing: "8 byte instruction identifier not provided", + _LangErrorCode.InstructionFallbackNotFound: "Fallback functions are not supported", + _LangErrorCode.InstructionDidNotDeserialize: ( + "The program could not deserialize the given instruction" + ), + _LangErrorCode.InstructionDidNotSerialize: ( + "The program could not serialize the given instruction" + ), + # Idl instructions. + _LangErrorCode.IdlInstructionStub: ( + "The program was compiled without idl instructions" + ), + _LangErrorCode.IdlInstructionInvalidProgram: ( + "The transaction was given an invalid program for the IDL instruction" + ), + # Constraints. + _LangErrorCode.ConstraintMut: "A mut constraint was violated", + _LangErrorCode.ConstraintHasOne: "A has_one constraint was violated", + _LangErrorCode.ConstraintSigner: "A signer constraint was violated", + _LangErrorCode.ConstraintRaw: "A raw constraint was violated", + _LangErrorCode.ConstraintOwner: "An owner constraint was violated", + _LangErrorCode.ConstraintRentExempt: "A rent exempt constraint was violated", + _LangErrorCode.ConstraintSeeds: "A seeds constraint was violated", + _LangErrorCode.ConstraintExecutable: "An executable constraint was violated", + _LangErrorCode.ConstraintState: "A state constraint was violated", + _LangErrorCode.ConstraintAssociated: "An associated constraint was violated", + _LangErrorCode.ConstraintAssociatedInit: ( + "An associated init constraint was violated" + ), + _LangErrorCode.ConstraintClose: "A close constraint was violated", + _LangErrorCode.ConstraintAddress: "An address constraint was violated", + _LangErrorCode.ConstraintZero: "Expected zero account discriminant", + _LangErrorCode.ConstraintTokenMint: "A token mint constraint was violated", + _LangErrorCode.ConstraintTokenOwner: "A token owner constraint was violated", + _LangErrorCode.ConstraintMintMintAuthority: ( + "A mint mint authority constraint was violated" + ), + _LangErrorCode.ConstraintMintFreezeAuthority: ( + "A mint freeze authority constraint was violated" + ), + _LangErrorCode.ConstraintMintDecimals: "A mint decimals constraint was violated", + _LangErrorCode.ConstraintSpace: "A space constraint was violated", + # Require. + _LangErrorCode.RequireViolated: "A require expression was violated", + _LangErrorCode.RequireEqViolated: "A require_eq expression was violated", + _LangErrorCode.RequireKeysEqViolated: "A require_keys_eq expression was violated", + _LangErrorCode.RequireNeqViolated: "A require_neq expression was violated", + _LangErrorCode.RequireKeysNeqViolated: "A require_keys_neq expression was violated", + _LangErrorCode.RequireGtViolated: "A require_gt expression was violated", + _LangErrorCode.RequireGteViolated: "A require_gte expression was violated", + # Accounts. + _LangErrorCode.AccountDiscriminatorAlreadySet: ( + "The account discriminator was already set on this account" + ), + _LangErrorCode.AccountDiscriminatorNotFound: ( + "No 8 byte discriminator was found on the account" + ), + _LangErrorCode.AccountDiscriminatorMismatch: ( + "8 byte discriminator did not match what was expected" + ), + _LangErrorCode.AccountDidNotDeserialize: "Failed to deserialize the account", + _LangErrorCode.AccountDidNotSerialize: "Failed to serialize the account", + _LangErrorCode.AccountNotEnoughKeys: ( + "Not enough account keys given to the instruction" + ), + _LangErrorCode.AccountNotMutable: "The given account is not mutable", + _LangErrorCode.AccountOwnedByWrongProgram: ( + "The given account is owned by a different program than expected" + ), + _LangErrorCode.InvalidProgramId: "Program ID was not as expected", + _LangErrorCode.InvalidProgramExecutable: "Program account is not executable", + _LangErrorCode.AccountNotSigner: "The given account did not sign", + _LangErrorCode.AccountNotSystemOwned: ( + "The given account is not owned by the system program" + ), + _LangErrorCode.AccountNotInitialized: ( + "The program expected this account to be already initialized" + ), + _LangErrorCode.AccountNotProgramData: ( + "The given account is not a program data account" + ), + _LangErrorCode.AccountNotAssociatedTokenAccount: ( + "The given account is not the associated token account" + ), + _LangErrorCode.AccountSysvarMismatch: ( + "The given public key does not match the required sysvar" + ), + # State. + _LangErrorCode.StateInvalidAddress: ( + "The given state account does not have the correct address" + ), + # Misc. + _LangErrorCode.Deprecated: ( + "The API being used is deprecated and should no longer be used" + ), +} + + +class ProgramError(Exception): + """An error from a user defined program.""" + + def __init__( + self, code: int, msg: Optional[str], logs: Optional[list[str]] = None + ) -> None: + """Init. + + Args: + code: The error code. + msg: The error message. + logs: The transaction simulation logs. + """ + self.code = code + self.msg = msg + self.logs = logs + super().__init__(f"{code}: {msg}") + + @classmethod + def parse( + cls, + err_info: RPCError, + idl_errors: dict[int, str], + program_id: Pubkey, + ) -> Optional[ProgramError]: + """Convert an RPC error into a ProgramError, if possible. + + Args: + err_info: The RPC error. + idl_errors: Errors from the IDL file. + program_id: The ID of the program we expect the error to come from. + + Returns: + A ProgramError or None. + """ + extracted = extract_code_and_logs(err_info, program_id) + if extracted is None: + return None + code, logs = extracted + msg = idl_errors.get(code) + if msg is not None: + return cls(code, msg, logs) + # parse framework internal error + msg = LangErrorMessage.get(code) + if msg is not None: + return cls(code, msg, logs) + # Unable to parse the error. + return None + + @classmethod + def parse_tx_error( + cls, + err_info: TransactionErrorType, + idl_errors: dict[int, str], + program_id: Pubkey, + logs: List[str], + ) -> Optional[ProgramError]: + """Convert an RPC error into a ProgramError, if possible. + + Args: + err_info: The RPC error. + idl_errors: Errors from the IDL file. + program_id: The ID of the program we expect the error to come from. + logs: The transaction logs. + + Returns: + A ProgramError or None. + """ + code = extract_code_tx_error(err_info, program_id, logs) + if code is None: + return None + msg = idl_errors.get(code) + if msg is not None: + return cls(code, msg, logs) + # parse framework internal error + msg = LangErrorMessage.get(code) + if msg is not None: + return cls(code, msg, logs) + # Unable to parse the error. + return None + + +error_re = re.compile(r"Program (\w+) failed: custom program error: (\w+)") + + +def _find_first_match(logs: list[str]) -> Optional[re.Match]: + for logline in logs: + first_match = error_re.match(logline) + if first_match is not None: + return first_match + return None + + +def extract_code_and_logs( + err_info: RPCError, program_id: Pubkey +) -> Optional[Tuple[int, List[str]]]: + """Extract the custom instruction error code from an RPC response. + + Args: + err_info: The RPC error. + program_id: The ID of the program we expect the error to come from. + """ + if isinstance(err_info, SendTransactionPreflightFailureMessage): + err_data = err_info.data + err_data_err = err_data.err + logs = err_data.logs + if logs is None: + return None + if err_data_err is None: + return None + maybe_code = _handle_ix_err(err_data_err, logs, program_id) + return None if maybe_code is None else (maybe_code, logs) + return None + + +def extract_code_tx_error( + err_info: TransactionErrorType, program_id: Pubkey, logs: List[str] +) -> Optional[int]: + """Extract the custom instruction error code from a transaction error. + + Args: + err_info: The tx error. + program_id: The ID of the program we expect the error to come from. + logs: The tx logs. + """ + return _handle_ix_err(err_info, logs, program_id) + + +def _handle_ix_err( + err: TransactionErrorType, logs: List[str], program_id: Pubkey +) -> Optional[int]: + if isinstance(err, TransactionErrorInstructionError): + instruction_err = err.err + if isinstance(instruction_err, InstructionErrorCustom): + code = instruction_err.code + first_match = _find_first_match(logs) + if first_match is None: + return None + program_id_raw, _ = first_match.groups() + if program_id_raw != str(program_id): + return None + return code + return None diff --git a/basics/anchorpy-main/src/anchorpy/idl.py b/basics/anchorpy-main/src/anchorpy/idl.py new file mode 100644 index 0000000..a4a27ee --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/idl.py @@ -0,0 +1,46 @@ +"""Contains code for parsing the IDL file.""" +from typing import Sequence, TypedDict + +import solders.pubkey +from anchorpy_core.idl import IdlTypeDefinition +from borsh_construct import U8, CStruct, Vec + +from anchorpy.borsh_extension import BorshPubkey + + +def _idl_address(program_id: solders.pubkey.Pubkey) -> solders.pubkey.Pubkey: + """Deterministic IDL address as a function of the program id. + + Args: + program_id: The program ID. + + Returns: + The public key of the IDL. + """ + base = solders.pubkey.Pubkey.find_program_address([], program_id)[0] + return solders.pubkey.Pubkey.create_with_seed(base, "anchor:idl", program_id) + + +class IdlProgramAccount(TypedDict): + """The on-chain account of the IDL.""" + + authority: solders.pubkey.Pubkey + data: bytes + + +IDL_ACCOUNT_LAYOUT = CStruct("authority" / BorshPubkey, "data" / Vec(U8)) + + +def _decode_idl_account(data: bytes) -> IdlProgramAccount: + """Decode on-chain IDL. + + Args: + data: binary data from the account that stores the IDL. + + Returns: + Decoded IDL. + """ + return IDL_ACCOUNT_LAYOUT.parse(data) + + +TypeDefs = Sequence[IdlTypeDefinition] diff --git a/basics/anchorpy-main/src/anchorpy/program/__init__.py b/basics/anchorpy-main/src/anchorpy/program/__init__.py new file mode 100644 index 0000000..46bbdd9 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/__init__.py @@ -0,0 +1 @@ +"""This subpacakge defines the `Program` class.""" diff --git a/basics/anchorpy-main/src/anchorpy/program/common.py b/basics/anchorpy-main/src/anchorpy/program/common.py new file mode 100644 index 0000000..8dd0979 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/common.py @@ -0,0 +1,90 @@ +"""Common utilities.""" +from dataclasses import dataclass +from typing import Any, Dict, NamedTuple, Tuple, Union, cast + +from anchorpy_core.idl import ( + IdlAccountItem, + IdlAccounts, + IdlInstruction, +) +from construct import Container +from pyheck import snake +from solders.pubkey import Pubkey + +from anchorpy.program.context import Accounts + +AddressType = Union[Pubkey, str] + + +class Event(NamedTuple): + """A parsed event object.""" + + name: str + data: Any + + +@dataclass +class NamedInstruction: + """Container for a named instruction. + + Attributes: + data: The actual instruction data. + name: The name of the instruction. + """ + + data: Union[Dict[str, Any], Container[Any]] + name: str + + +def _to_instruction(idl_ix: IdlInstruction, args: Tuple) -> NamedInstruction: + """Convert an IDL instruction and arguments to an Instruction object. + + Args: + idl_ix: The IDL instruction object. + args: The instruction arguments. + + Raises: + ValueError: If the incorrect number of arguments is provided. + + Returns: + The parsed Instruction object. + """ + if len(idl_ix.args) != len(args): + raise ValueError("Invalid argument length") + ix: Dict[str, Any] = {} + for idx, ix_arg in enumerate(idl_ix.args): + ix[snake(ix_arg.name)] = args[idx] + return NamedInstruction(data=ix, name=snake(idl_ix.name)) + + +def validate_accounts(ix_accounts: list[IdlAccountItem], accounts: Accounts): + """Check that accounts passed in `ctx` match the IDL. + + Args: + ix_accounts: Accounts from the IDL. + accounts: Accounts from the `ctx` arg. + + Raises: + ValueError: If `ctx` accounts don't match the IDL. + """ + for acc in ix_accounts: + acc_name = snake(acc.name) + if isinstance(acc, IdlAccounts): + nested = cast(Accounts, accounts[acc_name]) + validate_accounts(acc.accounts, nested) + elif acc_name not in accounts: + raise ValueError(f"Invalid arguments: {acc_name} not provided") + + +def translate_address(address: AddressType) -> Pubkey: + """Convert `str | Pubkey` into `Pubkey`. + + Args: + address: Public key as string or `Pubkey`. + + Returns: + Public key as `Pubkey`. + """ + if isinstance(address, str): + return Pubkey.from_string(address) + return address diff --git a/basics/anchorpy-main/src/anchorpy/program/context.py b/basics/anchorpy-main/src/anchorpy/program/context.py new file mode 100644 index 0000000..8da74e4 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/context.py @@ -0,0 +1,69 @@ +"""This module contains code handling the Context object.""" +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional, Tuple + +from anchorpy_core.idl import IdlInstruction +from pyheck import snake +from solana.rpc.types import TxOpts +from solders.instruction import AccountMeta, Instruction +from solders.keypair import Keypair + +from anchorpy.error import ArgsError + +# should be Dict[str, Union[Pubkey, Accounts]] +# but mypy doesn't support recursive types +Accounts = Dict[str, Any] + + +@dataclass +class Context: + """Context provides all non-argument inputs for generating Anchor transactions. + + Attributes: + accounts: The accounts used in the instruction context. + remaining_accounts: All accounts to pass into an instruction *after* the main + `accounts`. This can be used for optional or otherwise unknown accounts. + signers: Accounts that must sign a given transaction. + pre_instructions: Instructions to run *before* a given method. Often this is + used, for example to create accounts prior to executing a method. + post_instructions: Instructions to run *after* a given method. Often this is + used, for example to close accounts prior to executing a method. + options: Commitment parameters to use for a transaction. + + """ + + # For some reason mkdocstrings doesn't understand the full type hint + # here if we use list[Instruction] instead of typing.List. + # Weirdly there are other places where it understands list[whatever]. + + accounts: Accounts = field(default_factory=dict) + remaining_accounts: List[AccountMeta] = field(default_factory=list) + signers: List[Keypair] = field(default_factory=list) + pre_instructions: List[Instruction] = field(default_factory=list) + post_instructions: List[Instruction] = field(default_factory=list) + options: Optional[TxOpts] = None + + +def _check_args_length( + idl_ix: IdlInstruction, + args: Tuple, +) -> None: + """Check that the correct number of args is passed to the RPC function. + + Args: + idl_ix: The IDL instruction object. + args: The instruction arguments. + + Raises: + ArgsError: If the correct number of args is not parsed. + """ + if len(args) != len(idl_ix.args): + expected_arg_names = [snake(arg.name) for arg in idl_ix.args] + raise ArgsError( + f"Provided incorrect number of args to instruction={snake(idl_ix.name)}. " + f"Expected {expected_arg_names}", + f"Received {args}", + ) + + +EMPTY_CONTEXT = Context() diff --git a/basics/anchorpy-main/src/anchorpy/program/core.py b/basics/anchorpy-main/src/anchorpy/program/core.py new file mode 100644 index 0000000..65e69a4 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/core.py @@ -0,0 +1,264 @@ +"""This module defines the Program class.""" +from __future__ import annotations + +import zlib +from typing import Any, Optional + +from anchorpy_core.idl import Idl +from pyheck import snake +from solders.pubkey import Pubkey + +from anchorpy.coder.accounts import ACCOUNT_DISCRIMINATOR_SIZE +from anchorpy.coder.coder import Coder +from anchorpy.error import IdlNotFoundError +from anchorpy.idl import _decode_idl_account, _idl_address +from anchorpy.program.common import AddressType, translate_address +from anchorpy.program.namespace.account import AccountClient, _build_account +from anchorpy.program.namespace.instruction import ( + _InstructionFn, +) +from anchorpy.program.namespace.methods import ( + IdlFuncs, + MethodsBuilder, + _build_methods_item, +) +from anchorpy.program.namespace.rpc import ( + _build_rpc_item, + _RpcFn, +) +from anchorpy.program.namespace.simulate import ( + _build_simulate_item, + _SimulateFn, +) +from anchorpy.program.namespace.transaction import ( + _build_transaction_fn, + _TransactionFn, +) +from anchorpy.program.namespace.types import _build_types +from anchorpy.provider import Provider + + +def _parse_idl_errors(idl: Idl) -> dict[int, str]: + """Turn IDL errors into something readable. + + Uses message if available, otherwise name. + + Args: + idl: Parsed `Idl` instance. + + """ + errors = {} + idl_errors = idl.errors + if idl_errors is not None: + for e in idl_errors: + msg = e.msg if e.msg else e.name + errors[e.code] = msg + return errors + + +def _build_namespace( + idl: Idl, + coder: Coder, + program_id: Pubkey, + provider: Provider, +) -> tuple[ + dict[str, _RpcFn], + dict[str, _InstructionFn], + dict[str, _TransactionFn], + dict[str, AccountClient], + dict[str, _SimulateFn], + dict[str, Any], + dict[str, MethodsBuilder], +]: + """Generate all namespaces for a given program. + + Args: + idl: The parsed IDL object. + coder: The program's Coder object . + program_id: The Program ID. + provider: The program's provider. + + Returns: + The program namespaces. + """ + idl_errors = _parse_idl_errors(idl) + + rpc = {} + instruction = {} + transaction = {} + simulate = {} + methods = {} + + for idl_ix in idl.instructions: + + ix_item = _InstructionFn(idl_ix, coder.instruction.build, program_id) + tx_item = _build_transaction_fn(idl_ix, ix_item) + rpc_item = _build_rpc_item(idl_ix, tx_item, idl_errors, provider, program_id) + simulate_item = _build_simulate_item( + idl_ix, + tx_item, + idl_errors, + provider, + coder, + program_id, + idl, + ) + idl_funcs = IdlFuncs( + ix_fn=ix_item, tx_fn=tx_item, rpc_fn=rpc_item, simulate_fn=simulate_item + ) + methods_item = _build_methods_item(idl_funcs) + + name = snake(idl_ix.name) + instruction[name] = ix_item + transaction[name] = tx_item + rpc[name] = rpc_item + simulate[name] = simulate_item + methods[name] = methods_item + + account = _build_account(idl, coder, program_id, provider) if idl.accounts else {} + types = _build_types(idl) + return rpc, instruction, transaction, account, simulate, types, methods + + +def _pako_inflate(data): + # https://stackoverflow.com/questions/46351275/using-pako-deflate-with-python + decompress = zlib.decompressobj(15) + decompressed_data = decompress.decompress(data) + decompressed_data += decompress.flush() + return decompressed_data + + +class Program(object): + """Program provides the IDL deserialized client representation of an Anchor program. + + This API is the one stop shop for all things related to communicating with + on-chain programs. Among other things, one can send transactions, fetch + deserialized accounts, decode instruction data, subscribe to account + changes, and listen to events. + + In addition to field accessors and methods, the object provides a set of + dynamically generated properties, also known as namespaces, that + map one-to-one to program methods and accounts. + + """ + + def __init__( + self, idl: Idl, program_id: Pubkey, provider: Optional[Provider] = None + ): + """Initialize the Program object. + + Args: + idl: The parsed IDL object. + program_id: The program ID. + provider: The Provider object for the Program. Defaults to Provider.local(). + """ + self.idl = idl + self.program_id = program_id + self.provider = provider if provider is not None else Provider.local() + self.coder = Coder(idl) + + ( + rpc, + instruction, + transaction, + account, + simulate, + types, + methods, + ) = _build_namespace( + idl, + self.coder, + program_id, + self.provider, + ) + + self.rpc = rpc + self.instruction = instruction + self.transaction = transaction + self.account = account + self.simulate = simulate + self.type = types + self.methods = methods + + async def __aenter__(self) -> Program: + """Use as a context manager.""" + await self.provider.__aenter__() + return self + + async def __aexit__(self, _exc_type, _exc, _tb): + """Exit the context manager.""" + await self.close() + + async def close(self) -> None: + """Use this when you are done with the client.""" + await self.provider.close() + + @staticmethod + async def fetch_raw_idl( + address: AddressType, + provider: Provider, + ) -> str: + """Fetch an idl from the blockchain as a raw JSON dictionary. + + Args: + address: The program ID. + provider: The network and wallet context. + + Raises: + IdlNotFoundError: If the requested IDL account does not exist. + + Returns: + str: The raw IDL. + """ + program_id = translate_address(address) + actual_provider = provider if provider is not None else Provider.local() + idl_addr = _idl_address(program_id) + account_info = await actual_provider.connection.get_account_info(idl_addr) + account_info_val = account_info.value + if account_info_val is None: + raise IdlNotFoundError(f"IDL not found for program: {address}") + idl_account = _decode_idl_account( + account_info_val.data[ACCOUNT_DISCRIMINATOR_SIZE:] + ) + return _pako_inflate(bytes(idl_account["data"])).decode() + + @classmethod + async def fetch_idl( + cls, + address: AddressType, + provider: Provider, + ) -> Idl: + """Fetch and parse an idl from the blockchain. + + Args: + address: The program ID. + provider: The network and wallet context. + + Returns: + Idl: The fetched IDL. + """ + raw = await cls.fetch_raw_idl(address, provider) + return Idl.from_json(raw) + + @classmethod + async def at( + cls, + address: AddressType, + provider: Optional[Provider] = None, + ) -> Program: + """Generate a Program client by fetching the IDL from the network. + + In order to use this method, an IDL must have been previously initialized + via the anchor CLI's `anchor idl init` command. + + Args: + address: The program ID. + provider: The network and wallet context. + + Returns: + The Program instantiated using the fetched IDL. + """ + provider_to_use = Provider.local() if provider is None else provider + program_id = translate_address(address) + idl = await cls.fetch_idl(program_id, provider_to_use) + return cls(idl, program_id, provider) diff --git a/basics/anchorpy-main/src/anchorpy/program/event.py b/basics/anchorpy-main/src/anchorpy/program/event.py new file mode 100644 index 0000000..dbccfe1 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/event.py @@ -0,0 +1,166 @@ +"""This module contains code for handling Anchor events.""" +import binascii +from base64 import b64decode +from dataclasses import dataclass +from typing import Callable, List, Optional, cast + +from solders.pubkey import Pubkey + +from anchorpy.coder.coder import Coder +from anchorpy.program.common import Event + +PROGRAM_LOG = "Program log: " +PROGRAM_DATA = "Program data: " +PROGRAM_LOG_START_INDEX = len(PROGRAM_LOG) +PROGRAM_DATA_START_INDEX = len(PROGRAM_DATA) + + +class _ExecutionContext: + """Stack frame execution context, allowing one to track what program is executing for a given log.""" # noqa: E501 + + def __init__(self, log: str) -> None: + """Init. + + Args: + log: The log to process. + + Raises: + ValueError: If the log line is malformed. + """ + try: + program = log.split("Program ")[1].split(" invoke [")[0] + except IndexError as e: + raise ValueError("Could not find program invocation log line") from e + self.stack = [program] + + def program(self) -> str: + """Return the currently executing program. + + Returns: + The name of the program. + """ + return self.stack[-1] + + def push(self, new_program: str) -> None: + """Add to the stack. + + Args: + new_program: The program to add. + """ + self.stack.append(new_program) + + def pop(self) -> None: + """Pop from the stack.""" + self.stack.pop() + + +@dataclass +class EventParser: + """Parser to handle on_logs callbacks.""" + + program_id: Pubkey + coder: Coder + + def parse_logs(self, logs: List[str], callback: Callable[[Event], None]) -> None: + """Parse a list of logs using a provided callback. + + Args: + logs: The logs to parse. + callback: The function to handle the parsed log. + """ + log_scanner = _LogScanner(logs) + execution = _ExecutionContext(cast(str, log_scanner.to_next())) + log = log_scanner.to_next() + while log is not None: + event, new_program, did_pop = self.handle_log(execution, log) + if event is not None: + callback(event) + if new_program is not None: + execution.push(new_program) + if did_pop: + execution.pop() + log = log_scanner.to_next() + + def handle_log( + self, + execution: _ExecutionContext, + log: str, + ) -> tuple[Optional[Event], Optional[str], bool]: + """Main log handler. + + Args: + execution: The execution stack. + log: log string from the RPC node. + + Returns: + A three element array of the event, the next program + that was invoked for CPI, and a boolean indicating if + a program has completed execution (and thus should be popped off the + execution stack). + """ + # Executing program is this program. + if execution.stack and execution.program() == str(self.program_id): + return self.handle_program_log(log) + # Executing program is not this program. + return (None, *self.handle_system_log(log)) + + def handle_program_log( + self, log: str + ) -> tuple[Optional[Event], Optional[str], bool]: + """Handle logs from *this* program. + + Args: + log: log string from the RPC node. + + """ + # This is a `msg!` log or a `sol_log_data!` log. + if log.startswith(PROGRAM_LOG) or log.startswith(PROGRAM_DATA): + log_str = ( + log[PROGRAM_LOG_START_INDEX:] + if log.startswith(PROGRAM_LOG) + else log[PROGRAM_DATA_START_INDEX:] + ) + try: + decoded = b64decode(log_str) + except binascii.Error: + return None, None, False + event = self.coder.events.parse(decoded) + return event, None, False + return (None, *self.handle_system_log(log)) + + def handle_system_log(self, log: str) -> tuple[Optional[str], bool]: + """Handle logs when the current program being executing is *not* this. + + Args: + log: log string from the RPC node. + + """ + log_start = log.split(":")[0] + splitted = log_start.split(" ") + invoke_msg = f"Program {str(self.program_id)} invoke" + if len(splitted) == 3 and splitted[0] == "Program" and splitted[2] == "success": + return None, True + if log_start.startswith(invoke_msg): + return str(self.program_id), False + if "invoke" in log_start: + return "cpi", False + return None, False + + +@dataclass +class _LogScanner: + """Object that iterates over logs.""" + + logs: list[str] + + def to_next(self) -> Optional[str]: + """Move to the next log item. + + Returns: + The next log line, or None if there's nothing to return. + """ + if self.logs: + log = self.logs[0] + self.logs = self.logs[1:] + return log + return None diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/__init__.py b/basics/anchorpy-main/src/anchorpy/program/namespace/__init__.py new file mode 100644 index 0000000..cc1abae --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/__init__.py @@ -0,0 +1 @@ +"""This subpackage deals with the dynamic namespaces under `Program`.""" diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/account.py b/basics/anchorpy-main/src/anchorpy/program/namespace/account.py new file mode 100644 index 0000000..133e5fd --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/account.py @@ -0,0 +1,231 @@ +"""Provides the `AccountClient` class.""" +from dataclasses import dataclass +from typing import Any, Dict, List, Optional, Sequence, Union + +from anchorpy_core.idl import Idl, IdlTypeDefinition +from based58 import b58encode +from construct import Container +from solana.rpc.commitment import Commitment +from solana.rpc.types import MemcmpOpts +from solana.transaction import Instruction +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.system_program import CreateAccountParams, create_account + +from anchorpy.coder.accounts import ( + ACCOUNT_DISCRIMINATOR_SIZE, + _account_discriminator, +) +from anchorpy.coder.coder import Coder +from anchorpy.coder.common import _account_size +from anchorpy.error import AccountDoesNotExistError, AccountInvalidDiscriminator +from anchorpy.provider import Provider +from anchorpy.utils.rpc import get_multiple_accounts + + +def _build_account( + idl: Idl, + coder: Coder, + program_id: Pubkey, + provider: Provider, +) -> Dict[str, "AccountClient"]: + """Generate the `.account` namespace. + + Args: + idl: The parsed Idl object. + coder: The program's coder object. + program_id: The program ID. + provider: The Provider instance. + + Returns: + Mapping of account name to `AccountClient` instance. + """ + accounts_fns = {} + for idl_account in idl.accounts: + account_client = AccountClient(idl, idl_account, coder, program_id, provider) + accounts_fns[idl_account.name] = account_client + return accounts_fns + + +@dataclass +class ProgramAccount: + """Deserialized account owned by a program.""" + + public_key: Pubkey + account: Container + + +class AccountClient(object): + """Provides methods for fetching and creating accounts.""" + + def __init__( + self, + idl: Idl, + idl_account: IdlTypeDefinition, + coder: Coder, + program_id: Pubkey, + provider: Provider, + ): + """Init. + + Args: + idl: the parsed IDL object. + idl_account: the account definition from the IDL. + coder: The program's Coder object. + program_id: the program ID. + provider: The Provider object for the Program. + """ + self._idl_account = idl_account + self._program_id = program_id + self._provider = provider + self._coder = coder + self._size = ACCOUNT_DISCRIMINATOR_SIZE + _account_size(idl, idl_account) + + async def fetch( + self, address: Pubkey, commitment: Optional[Commitment] = None + ) -> Container[Any]: + """Return a deserialized account. + + Args: + address: The address of the account to fetch. + commitment: Bank state to query. + + + Raises: + AccountDoesNotExistError: If the account doesn't exist. + AccountInvalidDiscriminator: If the discriminator doesn't match the IDL. + """ + account_info = await self._provider.connection.get_account_info( + address, + encoding="base64", + commitment=commitment, + ) + if not account_info.value: + raise AccountDoesNotExistError(f"Account {address} does not exist") + data = account_info.value.data + discriminator = _account_discriminator(self._idl_account.name) + if discriminator != data[:ACCOUNT_DISCRIMINATOR_SIZE]: + msg = f"Account {address} has an invalid discriminator" + raise AccountInvalidDiscriminator(msg) + return self._coder.accounts.decode(data) + + async def fetch_multiple( + self, + addresses: List[Pubkey], + batch_size: int = 300, + commitment: Optional[Commitment] = None, + ) -> list[Optional[Container[Any]]]: + """Return multiple deserialized accounts. + + Accounts not found or with wrong discriminator are returned as None. + + Args: + addresses: The addresses of the accounts to fetch. + batch_size: The number of `getMultipleAccounts` objects to send + in each HTTP request. + commitment: Bank state to query. + """ + accounts = await get_multiple_accounts( + self._provider.connection, + addresses, + batch_size=batch_size, + commitment=commitment, + ) + discriminator = _account_discriminator(self._idl_account.name) + result: list[Optional[Container[Any]]] = [] + for account in accounts: + if account is None: + result.append(None) + elif discriminator == account.account.data[:8]: + result.append(self._coder.accounts.decode(account.account.data)) + else: + result.append(None) + return result + + async def create_instruction( + self, + signer: Keypair, + size_override: int = 0, + ) -> Instruction: + """Return an instruction for creating this account. + + Args: + signer: [description] + size_override: Optional override for the account size. Defaults to 0. + + Returns: + The instruction to create the account. + """ + space = size_override if size_override else self._size + mbre_resp = ( + await self._provider.connection.get_minimum_balance_for_rent_exemption( + space + ) + ) + return create_account( + CreateAccountParams( + from_pubkey=self._provider.wallet.public_key, + to_pubkey=signer.pubkey(), + space=space, + lamports=mbre_resp.value, + owner=self._program_id, + ) + ) + + async def all( # noqa: A003 + self, + buffer: Optional[bytes] = None, + filters: Optional[Sequence[Union[int, MemcmpOpts]]] = None, + ) -> list[ProgramAccount]: + """Return all instances of this account type for the program. + + Args: + buffer: bytes filter to append to the discriminator. + filters: (optional) Options to compare a provided series of bytes with + program account data at a particular offset. + Note: an int entry is converted to a `dataSize` filter. + """ + all_accounts = [] + discriminator = _account_discriminator(self._idl_account.name) + to_encode = discriminator if buffer is None else discriminator + buffer + bytes_arg = b58encode(to_encode).decode("ascii") + base_memcmp_opt = MemcmpOpts( + offset=0, + bytes=bytes_arg, + ) + filters_to_use = [base_memcmp_opt] + [] if filters is None else filters + resp = await self._provider.connection.get_program_accounts( + self._program_id, + encoding="base64", + commitment=self.provider.connection._commitment, + filters=filters_to_use, + ) + for r in resp.value: + account_data = r.account.data + all_accounts.append( + ProgramAccount( + public_key=r.pubkey, + account=self._coder.accounts.decode(account_data), + ), + ) + return all_accounts + + @property + def size(self) -> int: + """Return the number of bytes in this account.""" + return self._size + + @property + def program_id(self) -> Pubkey: + """Return the program ID owning all accounts.""" + return self._program_id + + @property + def provider(self) -> Provider: + """Return the client's wallet and network provider.""" + return self._provider + + @property + def coder(self) -> Coder: + """Return the coder.""" + return self._coder diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/instruction.py b/basics/anchorpy-main/src/anchorpy/program/namespace/instruction.py new file mode 100644 index 0000000..64b5176 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/instruction.py @@ -0,0 +1,127 @@ +"""This module deals with generating program instructions.""" +from typing import Any, Callable, Sequence, Tuple, cast + +from anchorpy_core.idl import IdlAccount, IdlAccountItem, IdlAccounts, IdlInstruction +from pyheck import snake +from solders.instruction import AccountMeta, Instruction +from solders.pubkey import Pubkey + +from anchorpy.program.common import ( + NamedInstruction, + _to_instruction, + validate_accounts, +) +from anchorpy.program.context import ( + EMPTY_CONTEXT, + Accounts, + Context, + _check_args_length, +) + + +class _InstructionFn: + """Callable object to create a `Instruction` generated from an IDL. + + Additionally it provides an `accounts` utility method, returning a list + of ordered accounts for the instruction. + """ + + def __init__( + self, + idl_ix: IdlInstruction, + encode_fn: Callable[[NamedInstruction], bytes], + program_id: Pubkey, + ) -> None: + """Init. + + Args: + idl_ix: IDL instruction object + encode_fn: [description] + program_id: The program ID. + + Raises: + ValueError: [description] + """ + if snake(idl_ix.name) == "_inner": + raise ValueError("_inner name is reserved") + self.idl_ix = idl_ix + self.encode_fn = encode_fn + self.program_id = program_id + + def __call__( + self, + *args: Any, + ctx: Context = EMPTY_CONTEXT, + ) -> Instruction: + """Create the Instruction. + + Args: + *args: The positional arguments for the program. The type and number + of these arguments depend on the program being used. + ctx: non-argument parameters to pass to the method. + """ + _check_args_length(self.idl_ix, args) + validate_accounts(self.idl_ix.accounts, ctx.accounts) + _validate_instruction(self.idl_ix, args) + + keys = self.accounts(ctx.accounts) + if ctx.remaining_accounts: + keys.extend(ctx.remaining_accounts) + return Instruction( + accounts=keys, + program_id=self.program_id, + data=self.encode_fn(_to_instruction(self.idl_ix, args)), + ) + + def accounts(self, accs: Accounts) -> list[AccountMeta]: + """Order the accounts for this instruction. + + Args: + accs: Accounts from `ctx` kwarg. + + Returns: + Ordered and flattened accounts. + """ + return _accounts_array(accs, self.idl_ix.accounts) + + +def _accounts_array( + ctx: Accounts, + accounts: Sequence[IdlAccountItem], +) -> list[AccountMeta]: + """Create a list of AccountMeta from a (possibly nested) dict of accounts. + + Args: + ctx: `accounts` field from the `Context` object. + accounts: accounts from the IDL. + + Returns: + AccountMeta objects. + """ + accounts_ret: list[AccountMeta] = [] + for acc in accounts: + if isinstance(acc, IdlAccounts): + rpc_accs = cast(Accounts, ctx[snake(acc.name)]) + acc_arr = _accounts_array(rpc_accs, acc.accounts) + accounts_ret.extend(acc_arr) + else: + account: IdlAccount = acc + single_account = cast(Pubkey, ctx[snake(account.name)]) + accounts_ret.append( + AccountMeta( + pubkey=single_account, + is_writable=account.is_mut, + is_signer=account.is_signer, + ), + ) + return accounts_ret + + +def _validate_instruction(ix: IdlInstruction, args: Tuple): # noqa: ARG001 + """Throws error if any argument required for the `ix` is not given. + + Args: + ix: The IDL instruction object. + args: The instruction arguments. + """ + # TODO: this isn't implemented in the TS client yet diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/methods.py b/basics/anchorpy-main/src/anchorpy/program/namespace/methods.py new file mode 100644 index 0000000..bec2a76 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/methods.py @@ -0,0 +1,158 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from solana.rpc import types +from solders.hash import Hash +from solders.instruction import AccountMeta, Instruction +from solders.keypair import Keypair +from solders.signature import Signature +from solders.transaction import VersionedTransaction + +from anchorpy.program.context import Accounts, Context +from anchorpy.program.namespace.instruction import _InstructionFn +from anchorpy.program.namespace.rpc import _RpcFn +from anchorpy.program.namespace.simulate import SimulateResponse, _SimulateFn +from anchorpy.program.namespace.transaction import _TransactionFn + + +@dataclass +class IdlFuncs: + ix_fn: _InstructionFn + tx_fn: _TransactionFn + rpc_fn: _RpcFn + simulate_fn: _SimulateFn + + +class MethodsBuilder: + def __init__( + self, + idl_funcs: IdlFuncs, + accounts: Accounts, + remaining_accounts: List[AccountMeta], + signers: List[Keypair], + pre_instructions: List[Instruction], + post_instructions: List[Instruction], + args: List[Any], + ) -> None: + self._idl_funcs = idl_funcs + self._accounts = accounts + self._remaining_accounts = remaining_accounts + self._signers = signers + self._pre_instructions = pre_instructions + self._post_instructions = post_instructions + self._args = args + + async def rpc(self, opts: Optional[types.TxOpts] = None) -> Signature: + ctx = self._build_context(opts) + return await self._idl_funcs.rpc_fn(*self._args, ctx=ctx) + + async def simulate(self, opts: Optional[types.TxOpts] = None) -> SimulateResponse: + ctx = self._build_context(opts) + return await self._idl_funcs.simulate_fn(*self._args, ctx=ctx) + + def instruction(self) -> Instruction: + ctx = self._build_context(opts=None) + return self._idl_funcs.ix_fn(*self._args, ctx=ctx) + + def transaction(self, payer: Keypair, blockhash: Hash) -> VersionedTransaction: + ctx = self._build_context(opts=None) + return self._idl_funcs.tx_fn( + *self._args, ctx=ctx, payer=payer, blockhash=blockhash + ) + + def pubkeys(self) -> Accounts: + return self._accounts + + def args(self, arguments: List[Any]) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=self._accounts, + remaining_accounts=self._remaining_accounts, + signers=self._signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions, + args=arguments, + ) + + def accounts(self, accs: Accounts) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=accs, + remaining_accounts=self._remaining_accounts, + signers=self._signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions, + args=self._args, + ) + + def signers(self, signers: List[Keypair]) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=self._accounts, + remaining_accounts=self._remaining_accounts, + signers=self._signers + signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions, + args=self._args, + ) + + def remaining_accounts(self, accounts: List[AccountMeta]) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=self._accounts, + remaining_accounts=self._remaining_accounts + accounts, + signers=self._signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions, + args=self._args, + ) + + def pre_instructions(self, ixs: List[Instruction]) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=self._accounts, + remaining_accounts=self._remaining_accounts, + signers=self._signers, + pre_instructions=self._pre_instructions + ixs, + post_instructions=self._post_instructions, + args=self._args, + ) + + def post_instructions(self, ixs: List[Instruction]) -> "MethodsBuilder": + idl_funcs = self._idl_funcs + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts=self._accounts, + remaining_accounts=self._remaining_accounts, + signers=self._signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions + ixs, + args=self._args, + ) + + def _build_context(self, opts: Optional[types.TxOpts]) -> Context: + return Context( + accounts=self._accounts, + remaining_accounts=self._remaining_accounts, + signers=self._signers, + pre_instructions=self._pre_instructions, + post_instructions=self._post_instructions, + options=opts, + ) + + +def _build_methods_item(idl_funcs: IdlFuncs) -> MethodsBuilder: + return MethodsBuilder( + idl_funcs=idl_funcs, + accounts={}, + remaining_accounts=[], + signers=[], + pre_instructions=[], + post_instructions=[], + args=[], + ) diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/rpc.py b/basics/anchorpy-main/src/anchorpy/program/namespace/rpc.py new file mode 100644 index 0000000..97da05f --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/rpc.py @@ -0,0 +1,71 @@ +"""This module contains code for generating RPC functions.""" +from typing import Any, Awaitable, Dict, Protocol + +from anchorpy_core.idl import IdlInstruction +from solana.rpc.commitment import Confirmed +from solana.rpc.core import RPCException +from solders.pubkey import Pubkey +from solders.signature import Signature + +from anchorpy.error import ProgramError +from anchorpy.program.context import EMPTY_CONTEXT, Context, _check_args_length +from anchorpy.program.namespace.transaction import _TransactionFn +from anchorpy.provider import Provider + + +class _RpcFn(Protocol): + """_RpcFn is a single RPC method generated from an IDL, sending a transaction paid for and signed by the configured provider.""" # noqa: E501 + + def __call__( + self, + *args: Any, + ctx: Context = EMPTY_CONTEXT, + ) -> Awaitable[Signature]: + """Call the function (this is just a protocol declaration). + + Args: + *args: The positional arguments for the program. The type and number + of these arguments depend on the program being used. + ctx: non-argument parameters to pass to the method. + """ + ... + + +def _build_rpc_item( # ts: RpcFactory + idl_ix: IdlInstruction, + tx_fn: _TransactionFn, + idl_errors: Dict[int, str], + provider: Provider, + program_id: Pubkey, +) -> _RpcFn: + """Build the function that sends transactions for the given method. + + Args: + idl_ix: The IDL instruction object. + tx_fn: The function that generates the `Transaction` to send. + idl_errors: Mapping of error code to error message. + provider: Anchor Provider instance. + program_id: The ID of the Anchor program. + + Returns: + The RPC function. + """ + + async def rpc_fn(*args: Any, ctx: Context = EMPTY_CONTEXT) -> Signature: + recent_blockhash = ( + await provider.connection.get_latest_blockhash(Confirmed) + ).value.blockhash + tx = tx_fn( + *args, payer=provider.wallet.payer, blockhash=recent_blockhash, ctx=ctx + ) + _check_args_length(idl_ix, args) + try: + return await provider.send(tx, ctx.options) + except RPCException as e: + err_info = e.args[0] + translated_err = ProgramError.parse(err_info, idl_errors, program_id) + if translated_err is not None: + raise translated_err from e + raise + + return rpc_fn diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/simulate.py b/basics/anchorpy-main/src/anchorpy/program/namespace/simulate.py new file mode 100644 index 0000000..d354cfc --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/simulate.py @@ -0,0 +1,94 @@ +"""This module contains code for creating simulate functions.""" +from typing import Any, Awaitable, Dict, NamedTuple, Protocol + +from anchorpy_core.idl import Idl, IdlInstruction +from solana.rpc.commitment import Confirmed +from solana.rpc.core import RPCException +from solders.pubkey import Pubkey + +from anchorpy.coder.coder import Coder +from anchorpy.error import ProgramError +from anchorpy.program.context import EMPTY_CONTEXT, Context, _check_args_length +from anchorpy.program.event import Event, EventParser +from anchorpy.program.namespace.transaction import _TransactionFn +from anchorpy.provider import Provider + + +class SimulateResponse(NamedTuple): + """The result of a simulate function call.""" + + events: list[Event] + raw: list[str] + + +class _SimulateFn(Protocol): + """A single method generated from an IDL. + + It simulates a method against a cluster configured by the provider, + returning a list of all the events and raw logs that were emitted + during the execution of the method. + """ + + def __call__( + self, + *args: Any, + ctx: Context = EMPTY_CONTEXT, + ) -> Awaitable[SimulateResponse]: + """Protocol definition. + + Args: + *args: The positional arguments for the program. The type and number + of these arguments depend on the program being used. + ctx: non-argument parameters to pass to the method. + + """ + + +def _build_simulate_item( + idl_ix: IdlInstruction, + tx_fn: _TransactionFn, + idl_errors: Dict[int, str], + provider: Provider, + coder: Coder, + program_id: Pubkey, + idl: Idl, +) -> _SimulateFn: + """Build the function to simulate transactions for a given method of a program. + + Args: + idl_ix: An IDL instruction object. + tx_fn: The function to generate the `Transaction` object. + idl_errors: Mapping of error code to message. + provider: A provider instance. + coder: The program's coder object. + program_id: The program ID. + idl: The parsed Idl instance. + + Returns: + The simulate function. + """ + + async def simulate_fn(*args: Any, ctx: Context = EMPTY_CONTEXT) -> SimulateResponse: + blockhash = ( + await provider.connection.get_latest_blockhash(Confirmed) + ).value.blockhash + tx = tx_fn(*args, payer=provider.wallet.payer, blockhash=blockhash, ctx=ctx) + _check_args_length(idl_ix, args) + resp = (await provider.simulate(tx, ctx.options)).value + resp_err = resp.err + logs = resp.logs or [] + if resp_err is None: + events = [] + if idl.events is not None: + parser = EventParser(program_id, coder) + parser.parse_logs(logs, lambda evt: events.append(evt)) + return SimulateResponse(events, logs) + else: + translated_err = ProgramError.parse_tx_error( + resp_err, idl_errors, program_id, logs + ) + if translated_err is not None: + raise translated_err + raise RPCException(resp_err) + + return simulate_fn diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/transaction.py b/basics/anchorpy-main/src/anchorpy/program/namespace/transaction.py new file mode 100644 index 0000000..635d114 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/transaction.py @@ -0,0 +1,65 @@ +"""This module deals with generating transactions.""" +from typing import Any, Protocol + +from anchorpy_core.idl import IdlInstruction +from more_itertools import unique_everseen +from solders.hash import Hash +from solders.instruction import Instruction +from solders.keypair import Keypair +from solders.message import Message +from solders.transaction import VersionedTransaction + +from anchorpy.program.context import EMPTY_CONTEXT, Context, _check_args_length +from anchorpy.program.namespace.instruction import _InstructionFn + + +class _TransactionFn(Protocol): + """A function to create a `Transaction` for a given program instruction.""" + + def __call__( + self, *args: Any, payer: Keypair, blockhash: Hash, ctx: Context = EMPTY_CONTEXT + ) -> VersionedTransaction: + """Make sure that the function looks like this. + + Args: + *args: The positional arguments for the program. The type and number + of these arguments depend on the program being used. + payer: The transaction fee payer. + blockhash: A recent blockhash. + ctx: non-argument parameters to pass to the method. + + """ + ... + + +# ts TransactionNamespaceFactory.build +def _build_transaction_fn( + idl_ix: IdlInstruction, ix_fn: _InstructionFn +) -> _TransactionFn: + """Build the function that generates Transaction objects. + + Args: + idl_ix: Instruction item from the IDL. + ix_fn (_InstructionFn): The function that generates instructions. + + Returns: + _TransactionFn: [description] + """ + + def tx_fn( + *args: Any, payer: Keypair, blockhash: Hash, ctx: Context = EMPTY_CONTEXT + ) -> VersionedTransaction: + ixns: list[Instruction] = [] + _check_args_length(idl_ix, args) + if ctx.pre_instructions: + ixns.extend(ctx.pre_instructions) + ixns.append(ix_fn(*args, ctx=ctx)) + if ctx.post_instructions: + ixns.extend(ctx.post_instructions) + ctx_signers = ctx.signers + signers = [] if ctx_signers is None else ctx_signers + all_signers = list(unique_everseen([payer, *signers])) + msg = Message.new_with_blockhash(ixns, payer.pubkey(), blockhash) + return VersionedTransaction(msg, all_signers) + + return tx_fn diff --git a/basics/anchorpy-main/src/anchorpy/program/namespace/types.py b/basics/anchorpy-main/src/anchorpy/program/namespace/types.py new file mode 100644 index 0000000..47b6711 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/program/namespace/types.py @@ -0,0 +1,27 @@ +"""This module contains code for handling user-defined types.""" +from typing import Any, Type + +from anchorpy_core.idl import Idl + +from anchorpy.coder.idl import _idl_typedef_to_python_type + + +def _build_types( + idl: Idl, +) -> dict[str, Type[Any]]: + """Generate the `.type` namespace. + + Args: + idl: A parsed `Idl` instance. + + Returns: + Mapping of type name to Python object. + """ + result = {} + for idl_type in idl.types: + try: + python_type = _idl_typedef_to_python_type(idl_type, idl.types) + except ValueError: + continue + result[idl_type.name] = python_type + return result diff --git a/basics/anchorpy-main/src/anchorpy/provider.py b/basics/anchorpy-main/src/anchorpy/provider.py new file mode 100644 index 0000000..507ccdb --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/provider.py @@ -0,0 +1,219 @@ +"""This module contains the Provider class and associated utilities.""" +from __future__ import annotations + +import json +from os import environ, getenv +from pathlib import Path +from types import MappingProxyType +from typing import List, Optional, Sequence, Union + +from solana.rpc import types +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Confirmed, Finalized, Processed +from solana.transaction import Transaction +from solders.keypair import Keypair +from solders.pubkey import Pubkey +from solders.rpc.responses import SimulateTransactionResp +from solders.signature import Signature +from solders.transaction import VersionedTransaction + +DEFAULT_OPTIONS = types.TxOpts(skip_confirmation=False, preflight_commitment=Processed) +COMMITMENT_RANKS = MappingProxyType({Processed: 0, Confirmed: 1, Finalized: 2}) + + +class Provider: + """The network and wallet context used to send transactions paid for and signed by the provider.""" # noqa: E501 + + def __init__( + self, + connection: AsyncClient, + wallet: Wallet, + opts: types.TxOpts = DEFAULT_OPTIONS, + ) -> None: + """Initialize the Provider. + + Args: + connection: The cluster connection where the program is deployed. + wallet: The wallet used to pay for and sign all transactions. + opts: Transaction confirmation options to use by default. + """ + self.connection = connection + self.wallet = wallet + self.opts = opts + + @classmethod + def local( + cls, url: Optional[str] = None, opts: types.TxOpts = DEFAULT_OPTIONS + ) -> Provider: + """Create a `Provider` with a wallet read from the local filesystem. + + Args: + url: The network cluster url. + opts: The default transaction confirmation options. + """ + connection = AsyncClient(url, opts.preflight_commitment) + wallet = Wallet.local() + return cls(connection, wallet, opts) + + @classmethod + def readonly( + cls, url: Optional[str] = None, opts: types.TxOpts = DEFAULT_OPTIONS + ) -> Provider: + """Create a `Provider` that can only fetch data, not send transactions. + + Args: + url: The network cluster url. + opts: The default transaction confirmation options. + """ + connection = AsyncClient(url, opts.preflight_commitment) + wallet = Wallet.dummy() + return cls(connection, wallet, opts) + + @classmethod + def env(cls) -> Provider: + """Create a `Provider` using the `ANCHOR_PROVIDER_URL` environment variable.""" + url = environ["ANCHOR_PROVIDER_URL"] + options = DEFAULT_OPTIONS + connection = AsyncClient(url, options.preflight_commitment) + wallet = Wallet.local() + return cls(connection, wallet, options) + + async def simulate( + self, + tx: Union[Transaction, VersionedTransaction], + opts: Optional[types.TxOpts] = None, + ) -> SimulateTransactionResp: + """Simulate the given transaction, returning emitted logs from execution. + + Args: + tx: The transaction to send. + signers: The set of signers in addition to the provider wallet that will + sign the transaction. + opts: Transaction confirmation options. + + Returns: + The transaction simulation result. + """ + if opts is None: + opts = self.opts + return await self.connection.simulate_transaction( + tx, sig_verify=True, commitment=opts.preflight_commitment + ) + + async def send( + self, + tx: Union[Transaction, VersionedTransaction], + opts: Optional[types.TxOpts] = None, + ) -> Signature: + """Send the given transaction, paid for and signed by the provider's wallet. + + Args: + tx: The transaction to send. + signers: The set of signers in addition to the provider wallet that will + sign the transaction. + opts: Transaction confirmation options. + + Returns: + The transaction signature from the RPC server. + """ + if opts is None: + opts = self.opts + raw = tx.serialize() if isinstance(tx, Transaction) else bytes(tx) + resp = await self.connection.send_raw_transaction(raw, opts=opts) + return resp.value + + async def send_all( + self, + txs: Sequence[Union[Transaction, VersionedTransaction]], + opts: Optional[types.TxOpts] = None, + ) -> list[Signature]: + """Similar to `send`, but for an array of transactions and signers. + + Args: + txs: a list of transaction objects. + opts: Transaction confirmation options. + + Returns: + The transaction signatures from the RPC server. + """ + if opts is None: + opts = self.opts + sigs = [] + for tx in txs: + raw = tx.serialize() if isinstance(tx, Transaction) else bytes(tx) + resp = await self.connection.send_raw_transaction(raw, opts=opts) + sigs.append(resp.value) + return sigs + + async def __aenter__(self) -> Provider: + """Use as a context manager.""" + await self.connection.__aenter__() + return self + + async def __aexit__(self, _exc_type, _exc, _tb): + """Exit the context manager.""" + await self.close() + + async def close(self) -> None: + """Use this when you are done with the connection.""" + await self.connection.close() + + +class Wallet: + """Python wallet object.""" + + def __init__(self, payer: Keypair): + """Initialize the wallet. + + Args: + payer: the Keypair used to sign transactions. + """ + self.payer = payer + + @property + def public_key(self) -> Pubkey: + """Get the public key of the wallet.""" + return self.payer.pubkey() + + def sign_transaction(self, tx: Transaction) -> Transaction: + """Sign a transaction using the wallet's keypair. + + Args: + tx: The transaction to sign. + + Returns: + The signed transaction. + """ + tx.sign(self.payer) + return tx + + def sign_all_transactions(self, txs: list[Transaction]) -> list[Transaction]: + """Sign a list of transactions using the wallet's keypair. + + Args: + txs: The transactions to sign. + + Returns: + The signed transactions. + """ + for tx in txs: + tx.sign_partial(self.payer) + return txs + + @classmethod + def local(cls) -> Wallet: + """Create a wallet instance from the filesystem. + + Uses the path at the ANCHOR_WALLET env var if set, + otherwise uses ~/.config/solana/id.json. + """ + path = Path(getenv("ANCHOR_WALLET", Path.home() / ".config/solana/id.json")) + with path.open() as f: + keypair: List[int] = json.load(f) + return cls(Keypair.from_bytes(keypair)) + + @classmethod + def dummy(cls) -> Wallet: + """Create a dummy wallet instance that won't be used to sign transactions.""" + keypair = Keypair.from_bytes([0] * 64) + return cls(keypair) diff --git a/basics/anchorpy-main/src/anchorpy/py.typed b/basics/anchorpy-main/src/anchorpy/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/basics/anchorpy-main/src/anchorpy/pytest_plugin.py b/basics/anchorpy-main/src/anchorpy/pytest_plugin.py new file mode 100644 index 0000000..ba876ff --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/pytest_plugin.py @@ -0,0 +1,296 @@ +"""This module provides the `localnet_fixture` fixture factory.""" +import os +import signal +import subprocess +from contextlib import suppress +from pathlib import Path +from typing import AsyncGenerator, Callable, Literal, Optional, Sequence, Tuple, Union + +from pytest import fixture +from pytest_asyncio import fixture as async_fixture +from pytest_xprocess import getrootdir +from solders.account import Account +from solders.pubkey import Pubkey +from xprocess import ProcessStarter, XProcess, XProcessInfo + +from anchorpy.program.core import Program +from anchorpy.workspace import close_workspace, create_workspace + +with suppress(ImportError): + from solders import bankrun + +_Scope = Literal["session", "package", "module", "class", "function"] + + +class _FixedXProcessInfo(XProcessInfo): + def terminate(self, timeout=60): # noqa: ARG002 + if not self.pid: + return 0 + try: + pgid = os.getpgid(self.pid) + except ProcessLookupError: + return 0 + try: + os.killpg(pgid, signal.SIGTERM) + except OSError as err: + print(f"Error while terminating process {err}") + return -1 + return 1 + + +class _FixedXProcess(XProcess): + def getinfo(self, name: str) -> _FixedXProcessInfo: + """Return Process Info for the given external process. + + Args: + name: Name of the external process. + """ + return _FixedXProcessInfo(self.rootdir, name) + + def ensure( + self, name: str, preparefunc: ProcessStarter, restart: bool = False + ) -> tuple: + """Return (PID, logfile) from a newly started or already running process. + + Args: + name: Name of the external process, used for caching info across test runs. + preparefunc: A subclass of ProcessStarter. + restart: Force restarting the process if it is running. + + Raises: + RuntimeError: If process fails to start within the required time. + + Returns: + (PID, logfile) logfile will be seeked to the end if the + server was running, otherwise seeked to the line after + where the wait pattern matched. + """ + info = self.getinfo(name) + if not restart and not info.isrunning(): + restart = True + + if restart: + # ensure the process is terminated first + if info.pid is not None: + info.terminate() + + controldir = info.controldir.ensure(dir=1) + starter = preparefunc(controldir, self) + args = [str(x) for x in starter.args] + self.log.debug("%s$ %s", controldir, " ".join(args)) + stdout = open(str(info.logpath), "wb", 0) # noqa: SIM115 + + # is env still necessary? we could pass all in popen_kwargs + kwargs = {"env": starter.env} + + popen_kwargs = { + "stdout": stdout, + "stderr": subprocess.STDOUT, + # this gives the user the ability to + # override the previous keywords if + # desired + **starter.popen_kwargs, + } + + kwargs["close_fds"] = True + + # keep references of all popen + # and info objects for cleanup + self._info_objects.append((info, starter.terminate_on_interrupt)) + popen_instance = subprocess.Popen( + args, + **popen_kwargs, + **kwargs, # type: ignore + ) + self._popen_instances.append(popen_instance) + + info.pid = pid = self._popen_instances[-1].pid + info.pidpath.write(str(pid)) + self.log.debug("process %r started pid=%s", name, pid) + stdout.close() + + # keep track of all file handles so we can + # cleanup later during teardown phase + self._file_handles.append(info.logpath.open()) + + if not restart: + self._file_handles[-1].seek(0, 2) + else: + if not starter.wait(self._file_handles[-1]): + raise RuntimeError( + f"Could not start process {name}, the specified " + f"log pattern was not found within {starter.max_read_lines} lines." + ) + self.log.debug("%s process startup detected", name) + + pytest_extlogfiles = self.config.__dict__.setdefault("_extlogfiles", {}) + pytest_extlogfiles[name] = self._file_handles[-1] + self.getinfo(name) + + return info.pid, info.logpath + + +@async_fixture(scope="session") +def _fixed_xprocess(request): + """Yield session-scoped XProcess helper to manage long-running processes required for testing.""" # noqa: E501 + rootdir = getrootdir(request.config) + with _FixedXProcess(request.config, rootdir) as xproc: + # pass in xprocess object into pytest_unconfigure + # through config for proper cleanup during teardown + request.config._xprocess = xproc + yield xproc + + +def localnet_fixture( + path: Path, + scope: _Scope = "module", + timeout_seconds: int = 60, + build_cmd: Optional[str] = None, +) -> Callable: + """Create a fixture that sets up and tears down a localnet instance with workspace programs deployed. + + Args: + path: Path to root of the Anchor project. + scope: Pytest fixture scope. + timeout_seconds: Time to wait for Anchor localnet to start. + build_cmd: Command to run before `anchor localnet`. Defaults to `anchor build`. + + Returns: + A localnet fixture for use with pytest. + """ # noqa: E501,D202 + + @fixture(scope=scope) + def _localnet_fixture(_fixed_xprocess): + class Starter(ProcessStarter): + # startup pattern + pattern = "JSON RPC URL" + terminate_on_interrupt = True + # command to start process + args = ["anchor", "localnet", "--skip-build"] + timeout = timeout_seconds + popen_kwargs = { + "cwd": path, + "start_new_session": True, + } + max_read_lines = 1_000 + # command to start process + + actual_build_cmd = "anchor build" if build_cmd is None else build_cmd + subprocess.run(actual_build_cmd, cwd=path, check=True, shell=True) + # ensure process is running and return its logfile + logfile = _fixed_xprocess.ensure("localnet", Starter) + + yield logfile + + # clean up whole process tree afterwards + _fixed_xprocess.getinfo("localnet").terminate() + + return _localnet_fixture + + +# Should figure out how to reuse localnet_fixture +# instead of copy-pasting (Pytest didn't like it). +def workspace_fixture( + path: Union[Path, str], + scope: _Scope = "module", + timeout_seconds: int = 60, + build_cmd: Optional[str] = None, +) -> Callable: + """Create a fixture that sets up and tears down a localnet instance and returns a workspace dict. + + Equivalent to combining `localnet_fixture`, `create_workspace` and `close_workspace`. + + Args: + path: Path to root of the Anchor project. + scope: Pytest fixture scope. + timeout_seconds: Time to wait for Anchor localnet to start. + build_cmd: Command to run before `anchor localnet`. Defaults to `anchor build`. + + Returns: + A workspace fixture for use with pytest. + """ # noqa: E501,D202 + + @async_fixture(scope=scope) + async def _workspace_fixture( + _fixed_xprocess, + ) -> AsyncGenerator[dict[str, Program], None]: + class Starter(ProcessStarter): + # startup pattern + pattern = "JSON RPC URL" + terminate_on_interrupt = True + # command to start process + args = ["anchor", "localnet", "--skip-build"] + timeout = timeout_seconds + popen_kwargs = { + "cwd": path, + "start_new_session": True, + } + max_read_lines = 1_000 + # command to start process + + actual_build_cmd = "anchor build" if build_cmd is None else build_cmd + subprocess.run(actual_build_cmd, cwd=path, check=True, shell=True) + # ensure process is running + _ = _fixed_xprocess.ensure("localnet", Starter) + ws = create_workspace(path) + yield ws + await close_workspace(ws) + + # clean up whole process tree afterwards + _fixed_xprocess.getinfo("localnet").terminate() + + return _workspace_fixture + + +async def _bankrun_helper( + path: Union[Path, str], + build_cmd: Optional[str] = None, + accounts: Optional[Sequence[Tuple[Pubkey, Account]]] = None, + compute_max_units: Optional[int] = None, + transaction_account_lock_limit: Optional[int] = None, +) -> "bankrun.ProgramTestContext": + actual_build_cmd = "anchor build" if build_cmd is None else build_cmd + subprocess.run(actual_build_cmd, cwd=path, check=True, shell=True) + path_to_use = Path(path) + return await bankrun.start_anchor( + path_to_use, + accounts=accounts, + compute_max_units=compute_max_units, + transaction_account_lock_limit=transaction_account_lock_limit, + ) + + +def bankrun_fixture( + path: Union[Path, str], + scope: _Scope = "module", + build_cmd: Optional[str] = None, + accounts: Optional[Sequence[Tuple[Pubkey, Account]]] = None, + compute_max_units: Optional[int] = None, + transaction_account_lock_limit: Optional[int] = None, +) -> "bankrun.ProgramTestContext": + """Create a fixture that builds the project and starts a bankrun with all the programs in the workspace deployed. + + Args: + path: Path to root of the Anchor project. + scope: Pytest fixture scope. + build_cmd: Command to build the project. Defaults to `anchor build`. + accounts: A sequence of (address, account_object) tuples, indicating + what data to write to the given addresses. + compute_max_units: Override the default compute unit limit for a transaction. + transaction_account_lock_limit: Override the default transaction account lock limit. + + Returns: + A bankrun fixture for use with pytest. + """ # noqa: E501,D202 + + @async_fixture(scope=scope) + async def _bankrun_fixture() -> bankrun.ProgramTestContext: + return await _bankrun_helper( + path, + build_cmd, + accounts, + compute_max_units, + transaction_account_lock_limit, + ) + + return _bankrun_fixture diff --git a/basics/anchorpy-main/src/anchorpy/template.py b/basics/anchorpy-main/src/anchorpy/template.py new file mode 100644 index 0000000..cf0a1ad --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/template.py @@ -0,0 +1,31 @@ +INIT_TESTS = '''"""AnchorPy integration tests.""" +import asyncio +from pytest import fixture, mark + +from anchorpy import Program +from anchorpy.pytest_plugin import workspace_fixture +from anchorpy.workspace import WorkspaceType + + +@fixture(scope="module") +def event_loop(): + """Create a module-scoped event loop so we can use module-scope async fixtures.""" + loop = asyncio.get_event_loop_policy().new_event_loop() # noqa: DAR301 + yield loop + loop.close() + + +workspace = workspace_fixture(".") + + +@fixture(scope="module") +def program(workspace: WorkspaceType) -> Program: + """Create a Program instance.""" + return workspace["{}"] + + +@mark.asyncio +async def test_init(program: Program) -> None: + """Test that the initialize function is invoked successfully.""" + await program.rpc["initialize"]() +''' diff --git a/basics/anchorpy-main/src/anchorpy/utils/__init__.py b/basics/anchorpy-main/src/anchorpy/utils/__init__.py new file mode 100644 index 0000000..d701d15 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/utils/__init__.py @@ -0,0 +1,4 @@ +"""Various utility functions.""" +from anchorpy.utils import rpc, token + +__all__ = ["rpc", "token"] diff --git a/basics/anchorpy-main/src/anchorpy/utils/rpc.py b/basics/anchorpy-main/src/anchorpy/utils/rpc.py new file mode 100644 index 0000000..12ab015 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/utils/rpc.py @@ -0,0 +1,157 @@ +"""This module contains the invoke function.""" +from asyncio import gather +from base64 import b64decode +from dataclasses import dataclass +from typing import Any, NamedTuple, Optional + +import jsonrpcclient +import zstandard +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Commitment +from solana.rpc.core import RPCException +from solana.transaction import Transaction +from solders.instruction import AccountMeta, Instruction +from solders.pubkey import Pubkey +from solders.signature import Signature +from toolz import concat, partition_all + +from anchorpy.program.common import AddressType, translate_address +from anchorpy.provider import Provider + +_GET_MULTIPLE_ACCOUNTS_LIMIT = 100 +_MAX_ACCOUNT_SIZE = 10 * 1048576 + + +class AccountInfo(NamedTuple): + """Information describing an account. + + Attributes: + executable: `True` if this account's data contains a loaded program. + owner: Identifier of the program that owns the account. + lamports: Number of lamports assigned to the account. + data: Optional data assigned to the account. + rent_epoch: Optional rent epoch info for for account. + """ + + executable: bool + owner: Pubkey + lamports: int + data: bytes + rent_epoch: Optional[int] + + +async def invoke( + program_id: AddressType, + provider: Provider, + accounts: Optional[list[AccountMeta]] = None, + data: Optional[bytes] = None, +) -> Signature: + """Send a transaction to a program with the given accounts and instruction data. + + Args: + program_id: The program ID + provider: the `Provider` instance. + accounts: `AccountMeta` objects. + data: The transaction data. + + Returns: + The transaction signature. + """ + translated_program_id = translate_address(program_id) + tx = Transaction() + tx.add( + Instruction( + program_id=translated_program_id, + accounts=[] if accounts is None else accounts, + data=bytes(0) if data is None else data, + ), + ) + return await provider.send(tx) + + +@dataclass +class _MultipleAccountsItem: + pubkey: Pubkey + account: AccountInfo + + +async def get_multiple_accounts( + connection: AsyncClient, + pubkeys: list[Pubkey], + batch_size: int = 3, + commitment: Optional[Commitment] = None, +) -> list[Optional[_MultipleAccountsItem]]: + """Fetch multiple account infos through batched `getMultipleAccount` RPC requests. + + Args: + connection: The `solana-py` client object. + pubkeys: Pubkeys to fetch. + batch_size: The number of `getMultipleAccount` objects to include in each + HTTP request. + commitment: Bank state to query. + + Returns: + Account infos and pubkeys. + """ + pubkeys_per_network_request = _GET_MULTIPLE_ACCOUNTS_LIMIT * batch_size + chunks = partition_all(pubkeys_per_network_request, pubkeys) + awaitables = [ + _get_multiple_accounts_core(connection, pubkeys_chunk, commitment) + for pubkeys_chunk in chunks + ] + results = await gather(*awaitables, return_exceptions=False) + return list(concat(results)) + + +async def _get_multiple_accounts_core( + connection: AsyncClient, pubkeys: list[Pubkey], commitment: Optional[Commitment] +) -> list[Optional[_MultipleAccountsItem]]: + pubkey_batches = partition_all(_GET_MULTIPLE_ACCOUNTS_LIMIT, pubkeys) + rpc_requests: list[dict[str, Any]] = [] + commitment_to_use = connection._commitment if commitment is None else commitment + for pubkey_batch in pubkey_batches: + pubkeys_to_send = [str(pubkey) for pubkey in pubkey_batch] + rpc_request = jsonrpcclient.request( + "getMultipleAccounts", + params=[ + pubkeys_to_send, + {"encoding": "base64+zstd", "commitment": commitment_to_use}, + ], + ) + rpc_requests.append(rpc_request) + resp = await connection._provider.session.post( + connection._provider.endpoint_uri, + json=rpc_requests, + headers={"content-encoding": "gzip"}, + ) + parsed = jsonrpcclient.parse(resp.json()) + result: list[Optional[_MultipleAccountsItem]] = [] + dctx = zstandard.ZstdDecompressor() + idx = 0 + for rpc_result in parsed: + if isinstance(rpc_result, jsonrpcclient.Error): + raise RPCException( + f"Failed to get info about accounts: {rpc_result.message}" + ) + for account in rpc_result.result["value"]: + if account is None: + result.append(None) + else: + acc_info_data = account["data"][0] + decoded = b64decode(acc_info_data) + decompressed = dctx.decompress( + decoded, max_output_size=_MAX_ACCOUNT_SIZE + ) + acc_info = AccountInfo( + executable=account["executable"], + owner=Pubkey.from_string(account["owner"]), + lamports=account["lamports"], + data=decompressed, + rent_epoch=account["rentEpoch"], + ) + multiple_accounts_item = _MultipleAccountsItem( + pubkey=pubkeys[idx], account=acc_info + ) + result.append(multiple_accounts_item) + idx += 1 + return result diff --git a/basics/anchorpy-main/src/anchorpy/utils/token.py b/basics/anchorpy-main/src/anchorpy/utils/token.py new file mode 100644 index 0000000..1289478 --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/utils/token.py @@ -0,0 +1,318 @@ +"""This module contains utilities for the SPL Token Program.""" +from typing import Optional + +from solana.rpc.commitment import Confirmed +from solders.instruction import Instruction +from solders.keypair import Keypair +from solders.message import Message +from solders.pubkey import Pubkey +from solders.rpc.responses import GetAccountInfoResp +from solders.system_program import CreateAccountParams, create_account +from solders.transaction import VersionedTransaction +from spl.token._layouts import ACCOUNT_LAYOUT, MINT_LAYOUT +from spl.token.async_client import AsyncToken +from spl.token.constants import TOKEN_PROGRAM_ID +from spl.token.core import AccountInfo, MintInfo +from spl.token.instructions import ( + InitializeAccountParams, + InitializeMintParams, + MintToParams, + initialize_account, + initialize_mint, + mint_to, +) + +from anchorpy.provider import Provider + + +async def create_token_account( + prov: Provider, + mint: Pubkey, + owner: Pubkey, +) -> Pubkey: + """Create a token account. + + Args: + prov: An anchorpy Provider instance. + mint: The pubkey of the token's mint. + owner: User account that will own the new account. + + Returns: + The pubkey of the new account. + """ + token = AsyncToken(prov.connection, mint, TOKEN_PROGRAM_ID, prov.wallet.payer) + return await token.create_account(owner) + + +async def create_token_account_instrs( + provider: Provider, + new_account_pubkey: Pubkey, + mint: Pubkey, + owner: Pubkey, +) -> tuple[Instruction, Instruction]: + """Generate instructions for creating a token account. + + Args: + provider: An anchorpy Provider instance. + new_account_pubkey: The pubkey of the new account. + mint: The pubkey of the token's mint. + owner: User account that will own the new account. + + Returns: + Transaction instructions to create the new account. + """ + mbre_resp = await provider.connection.get_minimum_balance_for_rent_exemption(165) + lamports = mbre_resp.value + return ( + create_account( + CreateAccountParams( + from_pubkey=provider.wallet.public_key, + to_pubkey=new_account_pubkey, + space=165, + lamports=lamports, + owner=TOKEN_PROGRAM_ID, + ) + ), + initialize_account( + InitializeAccountParams( + account=new_account_pubkey, + mint=mint, + owner=owner, + program_id=TOKEN_PROGRAM_ID, + ) + ), + ) + + +async def create_mint_and_vault( + provider: Provider, + amount: int, + owner: Optional[Pubkey] = None, + decimals: Optional[int] = None, +) -> tuple[Pubkey, Pubkey]: + """Create a mint and a vault, then mint tokens to the vault. + + Args: + provider: An anchorpy Provider instance. + amount: The amount of tokens to mint to the vault. + owner: User account that will own the new account. + decimals: The number of decimal places for the token to support. + + Returns: + The mint and vault pubkeys. + """ + actual_owner = provider.wallet.public_key if owner is None else owner + mint = Keypair() + vault = Keypair() + mint_space = 82 + create_mint_mbre_resp = ( + await provider.connection.get_minimum_balance_for_rent_exemption(mint_space) + ) + create_mint_mbre = create_mint_mbre_resp.value + create_mint_account_params = CreateAccountParams( + from_pubkey=provider.wallet.public_key, + to_pubkey=mint.pubkey(), + space=mint_space, + lamports=create_mint_mbre, + owner=TOKEN_PROGRAM_ID, + ) + create_mint_account_instruction = create_account( + create_mint_account_params, + ) + init_mint_instruction = initialize_mint( + InitializeMintParams( + mint=mint.pubkey(), + decimals=0 if decimals is None else decimals, + mint_authority=provider.wallet.public_key, + program_id=TOKEN_PROGRAM_ID, + ), + ) + vault_space = 165 + create_vault_mbre_resp = ( + await provider.connection.get_minimum_balance_for_rent_exemption(vault_space) + ) + create_vault_mbre = create_vault_mbre_resp.value + create_vault_account_instruction = create_account( + CreateAccountParams( + from_pubkey=provider.wallet.public_key, + to_pubkey=vault.pubkey(), + space=vault_space, + lamports=create_vault_mbre, + owner=TOKEN_PROGRAM_ID, + ), + ) + init_vault_instruction = initialize_account( + InitializeAccountParams( + program_id=TOKEN_PROGRAM_ID, + account=vault.pubkey(), + mint=mint.pubkey(), + owner=actual_owner, + ), + ) + mint_to_instruction = mint_to( + MintToParams( + program_id=TOKEN_PROGRAM_ID, + mint=mint.pubkey(), + dest=vault.pubkey(), + amount=amount, + mint_authority=provider.wallet.public_key, + ), + ) + blockhash = ( + await provider.connection.get_latest_blockhash(Confirmed) + ).value.blockhash + msg = Message.new_with_blockhash( + [ + create_mint_account_instruction, + init_mint_instruction, + create_vault_account_instruction, + init_vault_instruction, + mint_to_instruction, + ], + provider.wallet.public_key, + blockhash, + ) + tx = VersionedTransaction(msg, [provider.wallet.payer, mint, vault]) + await provider.send(tx) + return mint.pubkey(), vault.pubkey() + + +def parse_token_account(info: GetAccountInfoResp) -> AccountInfo: + """Parse `AccountInfo` from RPC response. + + Args: + info: the `get_account_info` RPC response. + + Raises: + ValueError: If the fetched data is the wrong size. + AttributeError: If the account is not owned by the token program. + + Returns: + The parsed `AccountInfo`. + """ + val = info.value + if not val: + raise ValueError("Invalid account owner") + + if val.owner != TOKEN_PROGRAM_ID: + raise AttributeError("Invalid account owner") + + bytes_data = val.data + if len(bytes_data) != ACCOUNT_LAYOUT.sizeof(): + raise ValueError("Invalid account size") + + decoded_data = ACCOUNT_LAYOUT.parse(bytes_data) + + mint = Pubkey(decoded_data.mint) + owner = Pubkey(decoded_data.owner) + amount = decoded_data.amount + + if decoded_data.delegate_option == 0: + delegate = None + delegated_amount = 0 + else: + delegate = Pubkey(decoded_data.delegate) + delegated_amount = decoded_data.delegated_amount + + is_initialized = decoded_data.state != 0 + is_frozen = decoded_data.state == 2 + + if decoded_data.is_native_option == 1: + rent_exempt_reserve = decoded_data.is_native + is_native = True + else: + rent_exempt_reserve = None + is_native = False + + if decoded_data.close_authority_option == 0: + close_authority = None + else: + close_authority = Pubkey(decoded_data.owner) + + return AccountInfo( + mint, + owner, + amount, + delegate, + delegated_amount, + is_initialized, + is_frozen, + is_native, + rent_exempt_reserve, + close_authority, + ) + + +async def get_token_account(provider: Provider, addr: Pubkey) -> AccountInfo: + """Retrieve token account information. + + Args: + provider: The anchorpy Provider instance. + addr: The pubkey of the token account. + + Returns: + The parsed `AccountInfo` of the token account. + """ + depositor_acc_info_raw = await provider.connection.get_account_info(addr) + return parse_token_account(depositor_acc_info_raw) + + +async def get_mint_info( + provider: Provider, + addr: Pubkey, +) -> MintInfo: + """Retrieve mint information. + + Args: + provider: The anchorpy Provider instance. + addr: The pubkey of the mint. + + Returns: + The parsed `MintInfo`. + """ + depositor_acc_info_raw = await provider.connection.get_account_info(addr) + return parse_mint_account(depositor_acc_info_raw) + + +def parse_mint_account(info: GetAccountInfoResp) -> MintInfo: + """Parse raw RPC response into `MintInfo`. + + Args: + info: The RPC response from calling `.get_account_info` for the mint pubkey. + + Raises: + AttributeError: If the account is not owned by the Token Program. + ValueError: If the fetched data is the wrong size. + + Returns: + The parsed `MintInfo`. + """ + val = info.value + if val is None: + raise ValueError("Account does not exist.") + owner = val.owner + if owner != TOKEN_PROGRAM_ID: + raise AttributeError(f"Invalid mint owner: {owner}") + + bytes_data = val.data + if len(bytes_data) != MINT_LAYOUT.sizeof(): + raise ValueError("Invalid mint size") + + decoded_data = MINT_LAYOUT.parse(bytes_data) + decimals = decoded_data.decimals + + mint_authority = ( + None + if decoded_data.mint_authority_option == 0 + else Pubkey(decoded_data.mint_authority) + ) + + supply = decoded_data.supply + is_initialized = decoded_data.is_initialized != 0 + + if decoded_data.freeze_authority_option == 0: + freeze_authority = None + else: + freeze_authority = Pubkey(decoded_data.freeze_authority) + + return MintInfo(mint_authority, supply, decimals, is_initialized, freeze_authority) diff --git a/basics/anchorpy-main/src/anchorpy/workspace.py b/basics/anchorpy-main/src/anchorpy/workspace.py new file mode 100644 index 0000000..276015c --- /dev/null +++ b/basics/anchorpy-main/src/anchorpy/workspace.py @@ -0,0 +1,52 @@ +"""This module contains code for creating the Anchor workspace.""" +from pathlib import Path +from typing import Dict, Optional, Union + +import toml # type: ignore +from anchorpy_core.idl import Idl +from solders.pubkey import Pubkey + +from anchorpy.program.core import Program +from anchorpy.provider import Provider + +WorkspaceType = Dict[str, Program] + + +def create_workspace( + path: Optional[Union[Path, str]] = None, url: Optional[str] = None +) -> WorkspaceType: + """Get a workspace from the provided path to the project root. + + Args: + path: The path to the project root. Defaults to the current working + directory if omitted. + url: The URL of the JSON RPC. Defaults to http://localhost:8899. + + Returns: + Mapping of program name to Program object. + """ + result = {} + project_root = Path.cwd() if path is None else Path(path) + idl_folder = project_root / "target/idl" + localnet_programs: dict[str, str] = toml.load(project_root / "Anchor.toml")[ + "programs" + ]["localnet"] + for file in idl_folder.iterdir(): + raw = file.read_text() + idl = Idl.from_json(raw) + name = idl.name + program_id = Pubkey.from_string(localnet_programs[name]) + program = Program(idl, program_id, Provider.local(url)) + result[idl.name] = program + return result + + +async def close_workspace(workspace: WorkspaceType) -> None: + """Close the HTTP clients of all the programs in the workspace. + + Args: + workspace: The workspace to close. + """ + for program in workspace.values(): + # could do this in a faster way but there's probably no point. + await program.close()