From 86856cfa4cdd83b578559b24950bb959c292c7ae Mon Sep 17 00:00:00 2001 From: MarkFeder Date: Mon, 31 Aug 2026 16:04:32 +0200 Subject: [PATCH 1/2] feat(token-swap): add pinocchio example --- Cargo.lock | 12 + Cargo.toml | 1 + README.md | 2 +- tokens/token-swap/pinocchio/cicd.sh | 8 + tokens/token-swap/pinocchio/package.json | 24 + tokens/token-swap/pinocchio/pnpm-lock.yaml | 2914 +++++++++++++++++ .../token-swap/pinocchio/program/Cargo.toml | 22 + .../token-swap/pinocchio/program/src/error.rs | 28 + .../program/src/instructions/create_amm.rs | 60 + .../program/src/instructions/create_pool.rs | 109 + .../src/instructions/deposit_liquidity.rs | 136 + .../pinocchio/program/src/instructions/mod.rs | 104 + .../swap_exact_tokens_for_tokens.rs | 151 + .../src/instructions/withdraw_liquidity.rs | 91 + .../token-swap/pinocchio/program/src/lib.rs | 12 + .../pinocchio/program/src/processor.rs | 43 + .../token-swap/pinocchio/program/src/state.rs | 100 + .../token-swap/pinocchio/program/src/util.rs | 38 + tokens/token-swap/pinocchio/tests/test.ts | 441 +++ tokens/token-swap/pinocchio/tsconfig.json | 15 + 20 files changed, 4310 insertions(+), 1 deletion(-) create mode 100644 tokens/token-swap/pinocchio/cicd.sh create mode 100644 tokens/token-swap/pinocchio/package.json create mode 100644 tokens/token-swap/pinocchio/pnpm-lock.yaml create mode 100644 tokens/token-swap/pinocchio/program/Cargo.toml create mode 100644 tokens/token-swap/pinocchio/program/src/error.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/create_amm.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/create_pool.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/mod.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs create mode 100644 tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs create mode 100644 tokens/token-swap/pinocchio/program/src/lib.rs create mode 100644 tokens/token-swap/pinocchio/program/src/processor.rs create mode 100644 tokens/token-swap/pinocchio/program/src/state.rs create mode 100644 tokens/token-swap/pinocchio/program/src/util.rs create mode 100644 tokens/token-swap/pinocchio/tests/test.ts create mode 100644 tokens/token-swap/pinocchio/tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 2a87face1..20039bc97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6046,6 +6046,18 @@ dependencies = [ "spl-token-2022-interface", ] +[[package]] +name = "token-swap-pinocchio-program" +version = "0.1.0" +dependencies = [ + "pinocchio", + "pinocchio-associated-token-account", + "pinocchio-log", + "pinocchio-system", + "pinocchio-token", + "solana-address 2.6.1", +] + [[package]] name = "toml" version = "0.8.23" diff --git a/Cargo.toml b/Cargo.toml index f703dde94..a62c50d23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ members = [ "tokens/token-2022/permanent-delegate/pinocchio/program", "tokens/token-2022/multiple-extensions/pinocchio/program", "tokens/token-2022/immutable-owner/pinocchio/program", + "tokens/token-swap/pinocchio/program", ] resolver = "2" diff --git a/README.md b/README.md index afbd9cdca..a8fbbd7f0 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ Create a fundraiser account specifying a target mint and amount, allowing contri [Create liquidity pools to allow trading of new digital assets and allows users that provide liquidity to be rewarded by creating an Automated Market Maker.](./tokens/token-swap/README.md) -[anchor](./tokens/token-swap/anchor) +[anchor](./tokens/token-swap/anchor) [pinocchio](./tokens/token-swap/pinocchio) ### External delegate token master diff --git a/tokens/token-swap/pinocchio/cicd.sh b/tokens/token-swap/pinocchio/cicd.sh new file mode 100644 index 000000000..e41db2140 --- /dev/null +++ b/tokens/token-swap/pinocchio/cicd.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This script is for quick building & deploying of the program. +# It also serves as a reference for the commands used for building & deploying Solana programs. +# Run this bad boy with "bash cicd.sh" or "./cicd.sh" + +cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so +solana program deploy ./program/target/so/*.so diff --git a/tokens/token-swap/pinocchio/package.json b/tokens/token-swap/pinocchio/package.json new file mode 100644 index 000000000..e872785e1 --- /dev/null +++ b/tokens/token-swap/pinocchio/package.json @@ -0,0 +1,24 @@ +{ + "type": "module", + "scripts": { + "test": "mocha --import=tsx -t 1000000 ./tests/test.ts", + "build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test", + "build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so", + "deploy": "solana program deploy ./program/target/so/*.so" + }, + "dependencies": { + "@solana-program/system": "^0.12.2", + "@solana-program/token-2022": "^0.12.0", + "@solana/kit": "^7.0.0", + "litesvm": "^1.3.0" + }, + "devDependencies": { + "@types/chai": "^5.2.3", + "@types/mocha": "^10.0.10", + "@types/node": "^26.1.0", + "chai": "^6.2.2", + "mocha": "^11.7.5", + "typescript": "^5.9.3", + "tsx": "^4.19.2" + } +} diff --git a/tokens/token-swap/pinocchio/pnpm-lock.yaml b/tokens/token-swap/pinocchio/pnpm-lock.yaml new file mode 100644 index 000000000..c3783c1f9 --- /dev/null +++ b/tokens/token-swap/pinocchio/pnpm-lock.yaml @@ -0,0 +1,2914 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@solana-program/system': + specifier: ^0.12.2 + version: 0.12.2(@solana/kit@7.1.1(typescript@5.9.3)) + '@solana-program/token-2022': + specifier: ^0.12.0 + version: 0.12.0(@solana/kit@7.1.1(typescript@5.9.3))(@solana/sysvars@8.2.0(typescript@5.9.3)) + '@solana/kit': + specifier: ^7.0.0 + version: 7.1.1(typescript@5.9.3) + litesvm: + specifier: ^1.3.0 + version: 1.4.1(typescript@5.9.3) + devDependencies: + '@types/chai': + specifier: ^5.2.3 + version: 5.2.3 + '@types/mocha': + specifier: ^10.0.10 + version: 10.0.10 + '@types/node': + specifier: ^26.1.0 + version: 26.4.0 + chai: + specifier: ^6.2.2 + version: 6.2.2 + mocha: + specifier: ^11.7.5 + version: 11.8.0 + tsx: + specifier: ^4.19.2 + version: 4.23.13 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@solana-program/system@0.12.2': + resolution: {integrity: sha512-MaBeOxlvTruQhA7UYkOb3hVTEHPPagOtd+PvTm6a8rGgvEAP0kD4BbC37NceOaR4ABNqdaCmD5OMVRKgrE6KAg==} + peerDependencies: + '@solana/kit': ^6.4.0 + + '@solana-program/system@0.14.0': + resolution: {integrity: sha512-Pjs2RINZHYmk/pqWNBCuQmfNjZT9woPJ/w7QkzzCSwcqcokbARtxsnIdgj4lJVxvr1DuxG8JGIbKnq6z1QRY6Q==} + peerDependencies: + '@solana/kit': ^8.0.0 + + '@solana-program/token-2022@0.12.0': + resolution: {integrity: sha512-SXkN6Epy9sC3OEELJLhc0hZtUijsAlR2Nb5gP6jcc0WVrtj5wEAmksWxF/yYrAB8AisJVgxvODkhIAnrd02DIw==} + engines: {node: '>=24.0.0'} + peerDependencies: + '@solana/kit': ^6.4.0 + '@solana/sysvars': ^5.0 + '@solana/zk-sdk': ^0.4.2 + peerDependenciesMeta: + '@solana/zk-sdk': + optional: true + + '@solana-program/token@0.16.0': + resolution: {integrity: sha512-VuFIu5vXsw1zwqls4/sGB88234oucmpuig553A33UnrbYnPZ8yXmqLYULBD92/k1pCGnMK+NMLWvsKzlZQ/1Kw==} + engines: {node: '>=24.0.0'} + peerDependencies: + '@solana/kit': ^8.0.0 + + '@solana-program/zk-elgamal-proof@0.2.0': + resolution: {integrity: sha512-znu1asnySKVp0VyOlfXCLZhpdWvyq/tJ7GRrX61Z6vyXXqJ/OMizv2BkgYL/VbzDKkFmUiYfiFwF3gDtZHv7VA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@7.1.1': + resolution: {integrity: sha512-7sy9VIFMdmu/7+2kVBRMU6mEvYx/DDjfam8DsBXbh+JszaTNZH3V8FutQYHRxrca3jxiumVfsy5USwKfVg8ElQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/accounts@8.2.0': + resolution: {integrity: sha512-An3BICrQJQ7jR5EIgXzYnaKyCE7+ZusW9xX8m6Vj7U2cKo8t6Y3PTQ+aMjEajwzsQfxEL8QnpClFC9hdoIu7MA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@7.1.1': + resolution: {integrity: sha512-/Tk2aTOT7UEcaJrdEcB3+SK09v5cZ/92NWqHBzEobxusTPNoeOFxa3MwV74Q1MgPecWdLz7TPreEhAKpcvV/vw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@8.2.0': + resolution: {integrity: sha512-7Okh8u7d3QrKu7ltJa3PASniUhasn1cdbcMQ/8gpGsmHQNCvdtAmvw18xSTdr4m62RJ/0cI/DtTVQyNwooeAXQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@7.1.1': + resolution: {integrity: sha512-tD07UKuw5i9Tw5xloVH+TUMrLzbHKixaB4DlGXumMEQU+JbYRPtFNqtMlrpVLuVM45nkbPfFp2Da0sXNRIqFHA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@8.2.0': + resolution: {integrity: sha512-izrnF8ZsviZDK0WCKl9ha5Ht4TNDHoU2QzMrPYOqkSkKkVh15p3MjTRXVFnM4CA+Xigtu8y8OPu2UnQNcAUVHQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@7.1.1': + resolution: {integrity: sha512-C1UOAQ7LH8RuCTfaij6hthTZeBlpp8GuD2g9Nag/xgiNKJGvAfVrcorb+kO/sufdYI8Lu+BiVvPeKOjQDQiKqg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@8.2.0': + resolution: {integrity: sha512-ORwzswFXbqNqlyVigogFIrn3Ge5cpDQkuMY0u3M40HBwHcC6a79uqM87DpoZypncKJDWA1DfJ/3w9hhTGumYAw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@7.1.1': + resolution: {integrity: sha512-MO+wMAuaatAQ96N3HhTsd7Uno+79rJw88P+OiU2n+pHK/rZuyu1yB2j12veqK4jUNWPR0aOyCuZ4rB2idrDjpg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@8.2.0': + resolution: {integrity: sha512-uq59oSAXM9QR+cO7R3JmSTxBiNNJnayVWz2VHBdhhCQS8DEmd6hhqY1RVmPTpFtE0Jix5MkRV3Bu5GTmf6Y66A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@7.1.1': + resolution: {integrity: sha512-TWWrQq6Wp5Yf5bSc6RbxDDYCRUBBmRtRgjvUMHGp0P9K+4uFwxllRjSZFzBPHgXj3UTeZmFJdcoD271QhAgibg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@8.2.0': + resolution: {integrity: sha512-GTOYzyk0SxQJS7MAN9zsyax4RFUtQDYzkVcpRf0Zo7eOy2/hPfuih09VJL2YdLPBJBcg8satdAYBYxxC282PYQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@7.1.1': + resolution: {integrity: sha512-6qWl+atG60D2LmP47GyGapQFhVsG1sVhVrEaM3lV09vwrGOMeOZARZ4ObaQ5m6uQmM04G+f8dY9d17nCMbGHGg==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5.4.0' + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs-strings@8.2.0': + resolution: {integrity: sha512-Dt/+rJ6gmH/OcOtvrhm6CtbB9jtVOGMxcIXoi62eNXw39Z1kJyN1gkqTjzBSS8Xb+X5lfkL70GHE9EDBb1JmqA==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5.4.0' + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs@7.1.1': + resolution: {integrity: sha512-1ZErMXzbbz7+jusem58dMO1haVWNlvFYKftc2dPhFu9MqlmZRfPS06GiZDjlLnD/6PHHBy7OKFMASoYw+fBAKg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs@8.2.0': + resolution: {integrity: sha512-kn2esyzFznx6Pbb+8FUe3YNKYRZUB8H81pCiXSIe1+0WJ44lRhZHMo0s4L3FQMKhWHOnlV30sWeIg8taHLVW7g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@7.1.1': + resolution: {integrity: sha512-q35qck8rBnNvJlPU00mnHEQm7gWvghzYz8khqVoLhjgodTzGp46VqnIOyI1LXOAF6XDal58bMNDO980MQgq8yw==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@8.2.0': + resolution: {integrity: sha512-BXc9mafl3SOnRL542jY02ezBSXHgqQOUCzREN/vvh1ASUPrUB3iorOEc62cSxFdDU4aDVMcugowJawmVNTXXSg==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@7.1.1': + resolution: {integrity: sha512-eVxOeAXYVBIWOIRfGwvuGQ6gPetQiiiOqSCK0xjwQo/yjXNVlSbpF6wLtQRnd3lbYkWsKdeV99FYe9cVY/g7/Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@8.2.0': + resolution: {integrity: sha512-OND76Qsng/fiTVSUvaNZFv0WKEULlwcGRtgeIE2keA2xYoHYNTSoD6mxv4iWANfeDo4EH7D/HnOwnGEFXMjr9w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fixed-points@7.1.1': + resolution: {integrity: sha512-qPj/V7kcFG/P0kuEa1U529+5L6mbkMwRIwvYBTDgBqPOt6wOdk9/c7Qn2VUQgSV0HgCXWxdHUdwaxBrYqO2ibg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fixed-points@8.2.0': + resolution: {integrity: sha512-w19JKErcbbENZzw4B+oBGwJXVKMIOi8TC7WxBawmpuv0XJSk6LP6surosg+XeoBHhtEsVCNHDtizi0mAS6OmMw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@7.1.1': + resolution: {integrity: sha512-AnFohvUHGrqAu3kTxxC0rZyU4JddA08hOUZ1/DT9XogCZWetBpNJktX9uNuXQe+sN4FKTX2MfL//iBFBAsxtDA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@8.2.0': + resolution: {integrity: sha512-+oTPl9yRZBbppUZU8qs4eu93iWPvs4Ij+HELPHISxLFo/cuG2YZGTDcWBVEzc8XEw0DUwwBOvou/wP7v1SnH+A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@7.1.1': + resolution: {integrity: sha512-KQGpsjeDflMcbKCdcF4KZVHcotYMS94DveRs0ZiBOsxb45dgkYrFmQ6ExJ/2exUOVF/rlp73knAP5ACrPMIAzA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@8.2.0': + resolution: {integrity: sha512-DDVHBAPifG3Q0s5e2hiBbfvrruLb3AhumTupDK+3f3f7MDc0Dth6rX054HtIINloqbFIw15f6/A3Z4VTh74CTg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@7.1.1': + resolution: {integrity: sha512-VxMpJI++RTwaqSBLIP1GTjOPLtlYOqxOJ93ug6DlSdu9jku8M/or77DiXDUi62VmEh3bbsECR646vTJXUaPArw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@8.2.0': + resolution: {integrity: sha512-SFts84wW6hDXGSrLK5spl8nbKxKns2aR+S2ar0Zi3I0bl007heyoO7UKyxAoUvhfhObIfEIMuy2/qLoo6vDcjQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@7.1.1': + resolution: {integrity: sha512-Rx+vzWAXUa/Ko7W6wG1HOG9B3nQFZ5dALz113WoAmBZf7iQvaLG7U4ECDM/ydR+Nbdy6wYww7zQKRIye+1d+Nw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@8.2.0': + resolution: {integrity: sha512-DezFZ0/ya98nILq+eSeq9fu9onVfqQYb7mtuDctdWxY1QFJvix562zkkFuFqnjOLL7XMEpdVPMxxQOzZ33wGCg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@7.1.1': + resolution: {integrity: sha512-By3kv5d8fIMr2SPmvI41hBXUwn0XuDu2MC8B7anaLHtY8MENTKhjg8sSfybf/RTH3387ErxhxmrAijTiHqTy/g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@8.2.0': + resolution: {integrity: sha512-1BfmnYmWe17u3RRX3HuNxjWkr2/n9Bai+yIG/XjMpgviobF3n8zsisZBJHPH2uORqvjkTFnOqGXoEQC8vUZHzw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@7.1.1': + resolution: {integrity: sha512-do4rmmOlSplVYN92zV6nROJxkiRn0c7juoH1lka2Pmq+cAC3QhQXKYAwWffTFYDJJDO9qCOh00uv2hhSZi6RDw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@8.2.0': + resolution: {integrity: sha512-iuDE6ewgT7LJOSVC6UK4HR6n8INujujbglYVVFWNK/xWKi5p1y8JCOEEWAl/htK26LhLC51A1nMNfmBmTjP3ZQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@7.1.1': + resolution: {integrity: sha512-Py0/8HaIF0y+KSXHAWdQYDdZlGNoaqOZonTFuGKyDsrkgvod3wRc0cpZ5I/D/Rei4tVpvjNUfCXLpyoiJAZ7kg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@8.2.0': + resolution: {integrity: sha512-yXfVtK1VoQ/Eyz2jH/1+avixhYcpCPkGkXuiXxA3Vf73WCvniC8mNVQ5ppsV+OqCNw62mxaFMmNiVgiAQapKuw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@7.1.1': + resolution: {integrity: sha512-iuPpfMbnRFjC4IoAIpKNA9UpizomxjW0E3F1LxUYQHXm1vCoF78kThp4qqgx2V3DmDFbhXGtXGSJPwmDdpLoBg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@8.2.0': + resolution: {integrity: sha512-b3//UzZe/uBaIpw4fT/nvCOghh5IHoNkW7FCMA/nzvQ48A16n8qpLki9/+lLHi0V0o7/Jz5Sof0UYc0aSf1Dig==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@7.1.1': + resolution: {integrity: sha512-35h7+QssfnT9Rwq5spUvdGc41WtTVWzJUCbiwvnUHtVwm3z96xSPwj765UtX/enfrcRHJRTDvej2ZjsvO1aeuQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@8.2.0': + resolution: {integrity: sha512-V7oSIhYXbUzjd5LUY4tbSUnyBka1hmULPFKuqxNzYxeY4eLI7S8GmTOg7xg6tOC0bJ+HLPvv64asuMXAn/PklQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-interfaces@7.1.1': + resolution: {integrity: sha512-2SHkGiftGmxg5xA5esxbwiY5wf+BOUsJG5rxD64/SHadUMaN3L0SDUSJfAXETJq5dCmVWxWGGPf2yVox4RIfdA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-interfaces@8.2.0': + resolution: {integrity: sha512-goOhGI493Fq+xhZ8JKl9/FDVFd0SJdkv5Lh0L2ubqekzUiGRCvHNwgXL8nVS7fso2UxIssDQmYKwXvubfhBVhw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/program-client-core@7.1.1': + resolution: {integrity: sha512-zoMq8Qg6psj6tFYpA35xKhSrF/LpdiVflV6nKohxZg3ocwtRqRhQfbY8/mjCA9EfH8vqvsn5il5CnxALRqmJJA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/program-client-core@8.2.0': + resolution: {integrity: sha512-miQ7qKW0d0etaa2YAehGU3heV5Y4ip5BBCwZXb/yg8yWndTt4d4E+8z+5Q/oS5kOuFraOFYr+Uev7SQKngNXBg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@7.1.1': + resolution: {integrity: sha512-nWpJKDBxj+cRpzH+lzdp+ztEm6MAothts56s7PIPFIKJe3zGRUpske0G3jIVHOGvtzCgQtmZUMCS1uBRiDRKDQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@8.2.0': + resolution: {integrity: sha512-yttl6ps7G9vM83pdfekyregTIqmRThFpdcrCIZ3y2qOLZ63KVxVMhNJ8kn8FrX3MyWh5rr/hTWdWKc4/U12Epg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@7.1.1': + resolution: {integrity: sha512-d3lhCfiFwiVyTRR6zhy1lcjDIh+l0a5gp1WPe64Vfa2gP0myC8P5C1Tknfjrp4d97DF3uBPqKAb5vV8hahNcNA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@8.2.0': + resolution: {integrity: sha512-i33ll+en6/Qcjw10gPeikCmU687W1pQNZPxirEuRWO/+PhE2qDQ9ZODgEakVi/1k+vSwL8sK941qfWig+3D0dQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@7.1.1': + resolution: {integrity: sha512-ELGIqNbz8apeAxCLdD1PEPAnu6KtgHmWvUbH4VqqNg2jgWquyUOfTI5rblvdflx2Hcb7GK05w8/iiUaknOQKnw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@8.2.0': + resolution: {integrity: sha512-lGvk1xeOo4cbypuD/5AAkJPHv5E3g7lqbzRIxZhsQPBkK+knVuEb7e3UncOnjuWkijZoFiB/k+Rfk83gmlhdpg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@7.1.1': + resolution: {integrity: sha512-DiQSj2rNnhOKOTs6YDjQ2y4KuOkv8lh6moLFiQnY0+TvanJrGrQjxLSrHdCUQxymQcUxzrraZL9k6vxNzId4gw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@8.2.0': + resolution: {integrity: sha512-JJ4BfUlX8eCIUh3zm8jTkHkNLf6HyY9UjJmi4GPicFPJZ5MXSUeh42eaSxvHszXUoS7u9V30kTt1hznwfF5fqA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@7.1.1': + resolution: {integrity: sha512-FDDgXfAPfq28sQNnGhZr/+2kp5QTTcNZ5m/Q9y9Jrlux6brdPsbf9Sh1Q/lgz7i76arzNW/rzRY125RzqGWANg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@8.2.0': + resolution: {integrity: sha512-pKOezoP0vuVwyjUKcS4Ewl3Mm/owv/16n7RocqFC3nx94qtk5FpSOAtielUHzBQ3JUgRLJrSKonw2yWxqgtVFg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@7.1.1': + resolution: {integrity: sha512-1FwhgL18qasRYlWffdBNIUoxQyGLzdh3YMQW3y33M61vk/q1ldEGJRypV9B/SrXmxwqDiUbQ+m+zgainCTp1ZA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@8.2.0': + resolution: {integrity: sha512-fR0hv/NP3K4WNePK9+97GYrDjzoN7kF0hlffRlSygksAC0FKvtKQ5lPBMg0ptWtswInp8Kk7Cz1zTCf6S+wLUQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@7.1.1': + resolution: {integrity: sha512-QTiQHmw2I/+fzeYsqII1guASiZskS7KZwPCI+6Arfk6Hxo9hBH8APuIu6uS8beSlVfnBCoOMybVDsnPF88fAoQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@8.2.0': + resolution: {integrity: sha512-37UHtjFmBUagtRw9NeWViTqSJlExyuI2j88m3jJSw9c6WRw7lLzUJIRGA51ArYyhAyhtAoGjXpUZiODKSE9lEw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@7.1.1': + resolution: {integrity: sha512-DRakQwjJsvbLDMDafMC9hM82YotUwSnrzqUDsrm6+e4YpKvzjXCyWlQ9kDJtrYeAA15sNVRpfs4L9Y7SoiWgrw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@8.2.0': + resolution: {integrity: sha512-R9pk8Lq030M1+bAz6e2qKv6ldGLmICFYJOaLxVifyDE4VBb1BGV3Tt1VTAaFIfHyYucmFmxGRmPFmedA3Kysqw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@7.1.1': + resolution: {integrity: sha512-yrqd+fV2Ae4hkzXX42aKRmTrz3FwvzKqO4h0ahs88dzSvXpy8l6Svu1k+uRgMlZ64g2P83jfQW+3Pj5fBoxmdw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@8.2.0': + resolution: {integrity: sha512-ajcncAnQ7XzE85MZ8uajjO9E7NyLwLWFVUYG+y82vb04ZWFRN3DrMswIC4T5m14OkU3RY/Z2WGxC9nT1W4wPWw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@7.1.1': + resolution: {integrity: sha512-5BaCgzPnf9WSEXBdASnM7iuPWtdrXKtG16qVTfm+9icLHDAkv2y62XF6XMSQQllH2k1RvLH1yOryZazAbaIyHA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@8.2.0': + resolution: {integrity: sha512-pHf/RiDqIHeGp2blseYRVjzWz4WUAY/5vkOya+BQhjx+7Odr/J19G3ZVDwExgtU95UhCDL9gVHPyAgKr0mNcUg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@7.1.1': + resolution: {integrity: sha512-k8a/JZFso/nvPBgurOGJ/ZC6sXCtYE3lCvTj95i29pl45C4SgjdetJrAH/pWEEhQXcxaJs7OLBGmCh2enazlJw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@8.2.0': + resolution: {integrity: sha512-tiMdJ9ZRgqANBNxlXK7OSDbwixAy7K1MzowzO7vNdlJbV+0WN0Lm/X5hEhKnlU42AD+clC6t9qZjVbg7BhQrqw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@7.1.1': + resolution: {integrity: sha512-Tljuh/sSkKMHrTqdYNaguUOkZ0T+Oj4+yHsUjKAzRsyffNLa67jx9gd5CtGfz3tpBpxDLVdNvdIaZgkhFwXk9g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@8.2.0': + resolution: {integrity: sha512-eOkgv52ZSMFLYNQaCsvxmPciZeXplWaPjbEcl9iZkGOAp3B2bO6KyMg8trEjaZNfcLC15+CpXMOZpMArXXSBCg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@7.1.1': + resolution: {integrity: sha512-yHlSUWgynaaqevuqipGhhcZnmFVNs+F6KIlbUZ0kQY6NMVtaSzx5AQrtQjbtAQzNRsRTDYkKuQg8nOruiDoseQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@8.2.0': + resolution: {integrity: sha512-rGF2vB8Bk0G2DYNxgEbDlANk+KirQGHVS1qRLxsi6OxACrgsSrGqrp0Onb5J8HLRMTEF+QrbbORDz9TQ5Q71fQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@7.1.1': + resolution: {integrity: sha512-yXEzCrLrWb1m5QqAJEGodJ9cqu5QiwfirSZTUWiMg1JtUl4uXOm1sqWQVLrwBz/+ctvtZTvG8+bQQAemVQiqPA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@8.2.0': + resolution: {integrity: sha512-Of/2KqDf728HxKzJlwlZRvRSrgpHoDAg5+t/3RwdXWBoRQ1r3FXqwBXOrBT2g47w+fUy1iJcy5XZ7LXnXBXPIg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@7.1.1': + resolution: {integrity: sha512-UfWYnAglm21q5hS3Op1bU5mYiWfx0VesovZcIur0uYPc3EJlfBVikl8pf745x+bB77xG6lIzNbb+99t48SQbkg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@8.2.0': + resolution: {integrity: sha512-wfLiDhtPMnE9Mn8IeSAJ0RMfCyHUqeNZllFLfgDY3RUsCf5s9EgMC5U+HNSvtEWJ8ABGWWR5nYyWMXTWiMeuPA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@7.1.1': + resolution: {integrity: sha512-l4Wzc2L+7WQPeC/kaCiia1aaUY/SJJdrNHnLibKchS2pb7YbF7J2OygsweHXliWCVpG0jZwcvIVpusgygbZQLw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@8.2.0': + resolution: {integrity: sha512-ATJBeJkaCUIxU3JWUcgfOjnOM3nn9qDfTigEkr0Cp3xFzIHnIVwis3W4Hcc/de1z0uvOXQOIYroahDr8H+djOg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@7.1.1': + resolution: {integrity: sha512-EscE/HKbBRKedG6AtQ0t9LOX87bw409viferac5YSuvuDxtZvbMpCCJs89uEQPVpgvHtjMJhfsjEwMK97uG9pA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@8.2.0': + resolution: {integrity: sha512-kwhOwBfUvUGuGGGnwTGHtNVQ02O3YCfXpp4xwUhe6bjUyp9M9wEmf70up6I9x2D4uB1nHaZRfco1CXfxhcH8sg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@7.1.1': + resolution: {integrity: sha512-Kb3V5smmMbvdft/Z/Iu9MlsF+i44f3GLK8c8TVu0+yHo41rF7OeTnOvlQ+uw1NpCfOto4GgPMFDXklIiKdauuA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@8.2.0': + resolution: {integrity: sha512-3L1LlMQ00l2kb3pcejvQ7vMKzyFayFhc6PqvZyh3M0/FNPfsc4z46yUf35I79SI6cuFWy2VkzA+a2ylOuCNC7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-introspection@7.1.1': + resolution: {integrity: sha512-o0f/wbwmgqRSqzr+RtCG8xZ5zTMVxnYv8LBmcDDCYvTeDPEF0EfHN8Oe28c7grQsXCIeaE+zXZ2p40lkTGZy4g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-introspection@8.2.0': + resolution: {integrity: sha512-unha6ugKrZa1TcB5Slnxisa/uQC56fyYi7BDvfCVI60OLEowtOTTvmJMKe35ETMzbimjBEPE/B5EgbydOHVBnQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@7.1.1': + resolution: {integrity: sha512-UBgd/TU0c8blrYDC9sD9P+wgmOOEK4OdODxD8CxB7oumN4ZAENv20u0AdZuvu1n4OwWwc1ikhtKjwg18e4wTIg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@8.2.0': + resolution: {integrity: sha512-+0U4iR/WRb8UiBXRhJJUY0+BmAOv+bQj7fifUF6o7x/IPg/K2Y4CmH4m7dOT2yhcg0KDzZPkXidXLm6saBX0Hw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@7.1.1': + resolution: {integrity: sha512-jop8y4+xiDJlocRLxqN++33Z5PDTe72HwmtgGrcIuGB4zq7U/lUX0uZM1JVcs9d3lJ3wBy2CcLTCyoYb78Swhg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@8.2.0': + resolution: {integrity: sha512-WfbzFTf2BvpW14OIOin/9Mj29XE5GJVORAnziDK+GEiTPnruNzNkHBrd2kt/ACg12dh12kmutDj5WI3pzrN3Bw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + + '@types/node@26.4.0': + resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.1.4: + resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + litesvm-darwin-arm64@1.4.1: + resolution: {integrity: sha512-6dofWLOxtknSL0W6N9W3f/kdnitsJ3xR0oE1W37CLcfd5kX4qCMVbaejZWJkLo4G2JzAk+hFZi965Z2OPyJACg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + litesvm-darwin-x64@1.4.1: + resolution: {integrity: sha512-Rjyg6SfnSKAO5/vMpnZpIHTcBWEUzjFj/GshXwzdyiWorrpC0poQjK3OJ9RJneJW2YDd+oUsGZCfwAplTXPGfg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + litesvm-linux-arm64-gnu@1.4.1: + resolution: {integrity: sha512-hyL/tcuFisAwNEwVzDGjtDRuLYDc+8PoX9QZU/M46vsLrdU2WCBU3g4WdV5z0z1nPl16TzcHVboqydyBy3ImFQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + litesvm-linux-arm64-musl@1.4.1: + resolution: {integrity: sha512-Folc4nWAXwkWvwK5iqf3C74eNAPRERg8Yn2QGVW+6pMgCXtaQt77PuIB9m32mLo+W+LZxiU+t3et0ZiFEm4YgQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + litesvm-linux-x64-gnu@1.4.1: + resolution: {integrity: sha512-sAKax22lAMS0DpWQcT6ICt8vs6phEjRJXT84LhhftnlNkcU03b0RJziFDr34LUTrkY5SMi/uqZEy0T86Xokg+w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + litesvm-linux-x64-musl@1.4.1: + resolution: {integrity: sha512-kKsOVhF7o8/sMiP6mgZCoIYr7zWEzwOBQdC0WrWp29JvOJpKOOgz3jioFdgWv/LnSYfkbJ+wZNWI8tMhAkmNOw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + litesvm@1.4.1: + resolution: {integrity: sha512-SGMdN6c44m5Deo27nZX7GIn42e/OlfQ4jIv0JIVxR3F5vU8ZbbjsLRGUj3b/r5jCITyN22u14JnuP+mhDyNLlg==} + engines: {node: '>= 20'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mocha@11.8.0: + resolution: {integrity: sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + tsx@4.23.13: + resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.10.0: + resolution: {integrity: sha512-ibvdovq3nCFs8Msrd95BW+zUOq+aOVbT+wpHUoPWhztbHEoPc6oof51iFDB6Es8lTKvNvVW9jNSAB8dwrKTMGg==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + workerpool@9.3.4: + resolution: {integrity: sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.8.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@solana-program/system@0.12.2(@solana/kit@7.1.1(typescript@5.9.3))': + dependencies: + '@solana/kit': 7.1.1(typescript@5.9.3) + + '@solana-program/system@0.14.0(@solana/kit@8.2.0(typescript@5.9.3))': + dependencies: + '@solana/kit': 8.2.0(typescript@5.9.3) + + '@solana-program/token-2022@0.12.0(@solana/kit@7.1.1(typescript@5.9.3))(@solana/sysvars@8.2.0(typescript@5.9.3))': + dependencies: + '@noble/curves': 1.9.7 + '@solana-program/zk-elgamal-proof': 0.2.0(@solana/kit@7.1.1(typescript@5.9.3)) + '@solana/kit': 7.1.1(typescript@5.9.3) + '@solana/sysvars': 8.2.0(typescript@5.9.3) + + '@solana-program/token@0.16.0(@solana/kit@8.2.0(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.14.0(@solana/kit@8.2.0(typescript@5.9.3)) + '@solana/kit': 8.2.0(typescript@5.9.3) + + '@solana-program/zk-elgamal-proof@0.2.0(@solana/kit@7.1.1(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.12.2(@solana/kit@7.1.1(typescript@5.9.3)) + '@solana/kit': 7.1.1(typescript@5.9.3) + + '@solana/accounts@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/accounts@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/addresses@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/addresses@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/assertions': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/assertions@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/assertions@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-core@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-core@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-data-structures@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-data-structures@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-numbers@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-numbers@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-strings@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-strings@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/fixed-points': 7.1.1(typescript@5.9.3) + '@solana/options': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/codecs@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/fixed-points': 8.2.0(typescript@5.9.3) + '@solana/options': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/errors@7.1.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 15.0.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/errors@8.2.0(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 15.0.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/fast-stable-stringify@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/fast-stable-stringify@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/fixed-points@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/fixed-points@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/functional@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/functional@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/instruction-plans@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/instruction-plans@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/instructions@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/instructions@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/keys@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/keys@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/assertions': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/kit@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 7.1.1(typescript@5.9.3) + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/instruction-plans': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/offchain-messages': 7.1.1(typescript@5.9.3) + '@solana/plugin-core': 7.1.1(typescript@5.9.3) + '@solana/plugin-interfaces': 7.1.1(typescript@5.9.3) + '@solana/program-client-core': 7.1.1(typescript@5.9.3) + '@solana/programs': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + '@solana/rpc': 7.1.1(typescript@5.9.3) + '@solana/rpc-api': 7.1.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/signers': 7.1.1(typescript@5.9.3) + '@solana/subscribable': 7.1.1(typescript@5.9.3) + '@solana/sysvars': 7.1.1(typescript@5.9.3) + '@solana/transaction-confirmation': 7.1.1(typescript@5.9.3) + '@solana/transaction-introspection': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/kit@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.2.0(typescript@5.9.3) + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/instruction-plans': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/offchain-messages': 8.2.0(typescript@5.9.3) + '@solana/plugin-core': 8.2.0(typescript@5.9.3) + '@solana/plugin-interfaces': 8.2.0(typescript@5.9.3) + '@solana/program-client-core': 8.2.0(typescript@5.9.3) + '@solana/programs': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + '@solana/rpc': 8.2.0(typescript@5.9.3) + '@solana/rpc-api': 8.2.0(typescript@5.9.3) + '@solana/rpc-parsed-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/signers': 8.2.0(typescript@5.9.3) + '@solana/subscribable': 8.2.0(typescript@5.9.3) + '@solana/sysvars': 8.2.0(typescript@5.9.3) + '@solana/transaction-confirmation': 8.2.0(typescript@5.9.3) + '@solana/transaction-introspection': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/nominal-types@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/nominal-types@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/offchain-messages@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/offchain-messages@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/options@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/options@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/plugin-core@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/plugin-core@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/plugin-interfaces@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 7.1.1(typescript@5.9.3) + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/instruction-plans': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/signers': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/plugin-interfaces@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.2.0(typescript@5.9.3) + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/instruction-plans': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/signers': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/program-client-core@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 7.1.1(typescript@5.9.3) + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/instruction-plans': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/plugin-interfaces': 7.1.1(typescript@5.9.3) + '@solana/rpc-api': 7.1.1(typescript@5.9.3) + '@solana/signers': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/program-client-core@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.2.0(typescript@5.9.3) + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/instruction-plans': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/plugin-interfaces': 8.2.0(typescript@5.9.3) + '@solana/rpc-api': 8.2.0(typescript@5.9.3) + '@solana/signers': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/programs@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/programs@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/promises@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/promises@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-api@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-transformers': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-api@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/rpc-parsed-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-parsed-types@7.1.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-parsed-types@8.2.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec-types@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec-types@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/subscribable': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/subscribable': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions-api@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-transformers': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-subscriptions-api@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-subscriptions-channel-websocket@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 7.1.1(typescript@5.9.3) + '@solana/subscribable': 7.1.1(typescript@5.9.3) + ws: 8.21.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@solana/rpc-subscriptions-channel-websocket@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.2.0(typescript@5.9.3) + '@solana/subscribable': 8.2.0(typescript@5.9.3) + ws: 8.21.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@solana/rpc-subscriptions-spec@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/subscribable': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions-spec@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/subscribable': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-transformers': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/subscribable': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/rpc-subscriptions@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/subscribable': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/rpc-transformers@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-transformers@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-transport-http@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + undici-types: 8.10.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-transport-http@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + undici-types: 8.10.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-types@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/fixed-points': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-types@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/fixed-points': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/rpc-api': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec': 7.1.1(typescript@5.9.3) + '@solana/rpc-spec-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-transformers': 7.1.1(typescript@5.9.3) + '@solana/rpc-transport-http': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/rpc-api': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec': 8.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.2.0(typescript@5.9.3) + '@solana/rpc-transport-http': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/signers@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + '@solana/offchain-messages': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/signers@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + '@solana/offchain-messages': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/subscribable@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/subscribable@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/sysvars@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/sysvars@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-confirmation@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/promises': 7.1.1(typescript@5.9.3) + '@solana/rpc': 7.1.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/transaction-confirmation@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/promises': 8.2.0(typescript@5.9.3) + '@solana/rpc': 8.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/transaction-introspection@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + '@solana/transactions': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-introspection@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + '@solana/transactions': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-messages@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-messages@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transactions@7.1.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 7.1.1(typescript@5.9.3) + '@solana/codecs-core': 7.1.1(typescript@5.9.3) + '@solana/codecs-data-structures': 7.1.1(typescript@5.9.3) + '@solana/codecs-numbers': 7.1.1(typescript@5.9.3) + '@solana/codecs-strings': 7.1.1(typescript@5.9.3) + '@solana/errors': 7.1.1(typescript@5.9.3) + '@solana/functional': 7.1.1(typescript@5.9.3) + '@solana/instructions': 7.1.1(typescript@5.9.3) + '@solana/keys': 7.1.1(typescript@5.9.3) + '@solana/nominal-types': 7.1.1(typescript@5.9.3) + '@solana/rpc-types': 7.1.1(typescript@5.9.3) + '@solana/transaction-messages': 7.1.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transactions@8.2.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.2.0(typescript@5.9.3) + '@solana/codecs-core': 8.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.2.0(typescript@5.9.3) + '@solana/codecs-strings': 8.2.0(typescript@5.9.3) + '@solana/errors': 8.2.0(typescript@5.9.3) + '@solana/functional': 8.2.0(typescript@5.9.3) + '@solana/instructions': 8.2.0(typescript@5.9.3) + '@solana/keys': 8.2.0(typescript@5.9.3) + '@solana/nominal-types': 8.2.0(typescript@5.9.3) + '@solana/rpc-types': 8.2.0(typescript@5.9.3) + '@solana/transaction-messages': 8.2.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/mocha@10.0.10': {} + + '@types/node@26.4.0': + dependencies: + undici-types: 8.3.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.3.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.1.4: + dependencies: + balanced-match: 1.0.2 + + browser-stdout@1.3.1: {} + + camelcase@6.3.0: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@15.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decamelize@4.0.0: {} + + diff@7.0.0: {} + + eastasianwidth@0.2.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat@5.0.2: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fsevents@2.3.3: + optional: true + + get-caller-file@2.0.5: {} + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + has-flag@4.0.0: {} + + he@1.2.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@2.1.0: {} + + is-unicode-supported@0.1.0: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + litesvm-darwin-arm64@1.4.1: + optional: true + + litesvm-darwin-x64@1.4.1: + optional: true + + litesvm-linux-arm64-gnu@1.4.1: + optional: true + + litesvm-linux-arm64-musl@1.4.1: + optional: true + + litesvm-linux-x64-gnu@1.4.1: + optional: true + + litesvm-linux-x64-musl@1.4.1: + optional: true + + litesvm@1.4.1(typescript@5.9.3): + dependencies: + '@solana-program/system': 0.14.0(@solana/kit@8.2.0(typescript@5.9.3)) + '@solana-program/token': 0.16.0(@solana/kit@8.2.0(typescript@5.9.3)) + '@solana/kit': 8.2.0(typescript@5.9.3) + optionalDependencies: + litesvm-darwin-arm64: 1.4.1 + litesvm-darwin-x64: 1.4.1 + litesvm-linux-arm64-gnu: 1.4.1 + litesvm-linux-arm64-musl: 1.4.1 + litesvm-linux-x64-gnu: 1.4.1 + litesvm-linux-x64-musl: 1.4.1 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + lru-cache@10.4.3: {} + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.4 + + minipass@7.1.3: {} + + mocha@11.8.0: + dependencies: + browser-stdout: 1.3.1 + chokidar: 4.0.3 + debug: 4.4.3(supports-color@8.1.1) + diff: 7.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 10.5.0 + he: 1.2.0 + is-path-inside: 3.0.3 + js-yaml: 4.3.2 + log-symbols: 4.1.0 + minimatch: 9.0.9 + ms: 2.1.3 + picocolors: 1.1.1 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 9.3.4 + yargs: 17.7.3 + yargs-parser: 21.1.1 + yargs-unparser: 2.0.0 + + ms@2.1.3: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + picocolors@1.1.1: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + readdirp@4.1.2: {} + + require-directory@2.1.1: {} + + safe-buffer@5.2.1: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + tsx@4.23.13: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + undici-types@8.10.0: {} + + undici-types@8.3.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + workerpool@9.3.4: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + ws@8.21.3: {} + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} diff --git a/tokens/token-swap/pinocchio/program/Cargo.toml b/tokens/token-swap/pinocchio/program/Cargo.toml new file mode 100644 index 000000000..42e052f8d --- /dev/null +++ b/tokens/token-swap/pinocchio/program/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "token-swap-pinocchio-program" +version = "0.1.0" +edition = "2021" + +[dependencies] +pinocchio.workspace = true +solana-address.workspace = true +pinocchio-log.workspace = true +pinocchio-system.workspace = true +pinocchio-token.workspace = true +pinocchio-associated-token-account.workspace = true + +[lib] +crate-type = ["cdylib", "lib"] + +[features] +custom-heap = [] +custom-panic = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] } diff --git a/tokens/token-swap/pinocchio/program/src/error.rs b/tokens/token-swap/pinocchio/program/src/error.rs new file mode 100644 index 000000000..62ffa6d6f --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/error.rs @@ -0,0 +1,28 @@ +use pinocchio::error::ProgramError; + +/// Errors returned by this example, surfaced as `ProgramError::Custom`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SwapError { + /// The fee is not below 100%. + InvalidFee = 0, + /// A mint does not match the one the pool was created for. + InvalidMint = 1, + /// The first deposit must exceed the locked minimum liquidity. + DepositTooSmall = 2, + /// The trade would return less than the caller asked for. + OutputTooSmall = 3, + /// The constant-product invariant would fall. + InvariantViolated = 4, + /// An arithmetic step overflowed or divided by zero. + MathOverflow = 5, + /// An account is not the PDA this program derives for it. + InvalidSeeds = 6, + /// An account is not owned by this program, or is the wrong size. + InvalidAccountData = 7, +} + +impl From for ProgramError { + fn from(error: SwapError) -> Self { + ProgramError::Custom(error as u32) + } +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/create_amm.rs b/tokens/token-swap/pinocchio/program/src/instructions/create_amm.rs new file mode 100644 index 000000000..2d2c514a6 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/create_amm.rs @@ -0,0 +1,60 @@ +use pinocchio::{cpi::Seed, error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_log::log; + +use crate::{ + error::SwapError, + instructions::expect_pda, + state::{write_amm, AMM_SIZE, MAX_FEE_BASIS_POINTS}, + util::create_pda_account, +}; + +/// Creates an AMM: a namespace for pools, carrying the fee every trade pays. +/// +/// The `id` is chosen by the caller and is the AMM's only seed, so one deployed +/// program can host many independent AMMs. +/// +/// Accounts: +/// 0. `[writable]` amm (PDA `[id]`) +/// 1. `[]` admin +/// 2. `[signer, writable]` payer +/// 3. `[]` system program +/// +/// Instruction data: `[id: [u8; 32], fee: u16 (LE)]` +pub fn create_amm(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [amm, admin, payer, _system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !payer.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let id: [u8; 32] = data + .get(..32) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?; + let fee = u16::from_le_bytes( + data.get(32..34) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + + // Fees are basis points; at 10000 a trade would take the entire input. + if fee >= MAX_FEE_BASIS_POINTS { + return Err(SwapError::InvalidFee.into()); + } + + let bump = expect_pda(program_id, amm, &[&id])?; + let bump_bytes = [bump]; + let seeds = [Seed::from(&id), Seed::from(&bump_bytes)]; + + log!("Creating AMM"); + create_pda_account(payer, amm, AMM_SIZE, program_id, &seeds)?; + + write_amm(&mut amm.try_borrow_mut()?, &id, admin.address(), fee)?; + + log!("AMM created"); + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/create_pool.rs b/tokens/token-swap/pinocchio/program/src/instructions/create_pool.rs new file mode 100644 index 000000000..e6fb7b531 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/create_pool.rs @@ -0,0 +1,109 @@ +use pinocchio::{cpi::Seed, error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_associated_token_account::instructions::Create; +use pinocchio_log::log; +use pinocchio_token::instructions::InitializeMint2; + +use crate::{ + error::SwapError, + instructions::expect_pda, + state::{read_amm, write_pool, AUTHORITY_SEED, LIQUIDITY_DECIMALS, LIQUIDITY_SEED, MINT_SIZE, POOL_SIZE}, + util::create_pda_account, +}; + +/// Creates a pool for a mint pair under an existing AMM. +/// +/// Everything the pool owns hangs off one authority PDA — the two vaults and +/// the liquidity mint — so the program can move pool funds without any wallet +/// holding that power. +/// +/// Accounts: +/// 0. `[]` amm (PDA `[id]`) +/// 1. `[writable]` pool (PDA `[amm, mint_a, mint_b]`) +/// 2. `[]` pool authority (PDA `[amm, mint_a, mint_b, b"authority"]`) +/// 3. `[writable]` liquidity mint (PDA `[amm, mint_a, mint_b, b"liquidity"]`) +/// 4. `[]` mint A +/// 5. `[]` mint B +/// 6. `[writable]` pool's token A account (ATA of the authority) +/// 7. `[writable]` pool's token B account (ATA of the authority) +/// 8. `[signer, writable]` payer +/// 9. `[]` system program +/// 10. `[]` SPL Token program +/// 11. `[]` associated token program +/// +/// Instruction data: none beyond the discriminator. +pub fn create_pool(program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult { + let [amm, pool, pool_authority, mint_liquidity, mint_a, mint_b, pool_account_a, pool_account_b, payer, system_program, token_program, _associated_token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !payer.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + if !amm.owned_by(program_id) { + return Err(SwapError::InvalidAccountData.into()); + } + + // The AMM's id is its own seed, so rederiving from the stored id proves the + // account really is an AMM this program created. + let id = read_amm(&amm.try_borrow()?)?.id; + expect_pda(program_id, amm, &[&id])?; + + let amm_key = *amm.address(); + let a = *mint_a.address(); + let b = *mint_b.address(); + + let pool_bump = expect_pda(program_id, pool, &[amm_key.as_ref(), a.as_ref(), b.as_ref()])?; + expect_pda(program_id, pool_authority, &[amm_key.as_ref(), a.as_ref(), b.as_ref(), AUTHORITY_SEED])?; + let liquidity_bump = + expect_pda(program_id, mint_liquidity, &[amm_key.as_ref(), a.as_ref(), b.as_ref(), LIQUIDITY_SEED])?; + + log!("Creating pool"); + let pool_bump_bytes = [pool_bump]; + let pool_seeds = + [Seed::from(amm_key.as_ref()), Seed::from(a.as_ref()), Seed::from(b.as_ref()), Seed::from(&pool_bump_bytes)]; + create_pda_account(payer, pool, POOL_SIZE, program_id, &pool_seeds)?; + write_pool(&mut pool.try_borrow_mut()?, &amm_key, &a, &b)?; + + log!("Creating liquidity mint"); + let liquidity_bump_bytes = [liquidity_bump]; + let liquidity_seeds = [ + Seed::from(amm_key.as_ref()), + Seed::from(a.as_ref()), + Seed::from(b.as_ref()), + Seed::from(LIQUIDITY_SEED), + Seed::from(&liquidity_bump_bytes), + ]; + create_pda_account(payer, mint_liquidity, MINT_SIZE, token_program.address(), &liquidity_seeds)?; + InitializeMint2 { + mint: mint_liquidity, + decimals: LIQUIDITY_DECIMALS, + mint_authority: pool_authority.address(), + freeze_authority: None, + } + .invoke()?; + + log!("Creating pool vaults"); + Create { + funding_account: payer, + account: pool_account_a, + wallet: pool_authority, + mint: mint_a, + system_program, + token_program, + } + .invoke()?; + Create { + funding_account: payer, + account: pool_account_b, + wallet: pool_authority, + mint: mint_b, + system_program, + token_program, + } + .invoke()?; + + log!("Pool created"); + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs b/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs new file mode 100644 index 000000000..52423b135 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs @@ -0,0 +1,136 @@ +use pinocchio::{cpi::Signer, error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_associated_token_account::instructions::CreateIdempotent; +use pinocchio_log::log; +use pinocchio_token::instructions::{MintTo, Transfer}; + +use crate::{ + error::SwapError, + instructions::{mul_div, PoolSeeds}, + state::{token_amount, MINIMUM_LIQUIDITY}, +}; + +/// Adds liquidity, minting LP tokens in return. +/// +/// Deposits must match the pool's current ratio, so the depositor cannot move +/// the price by adding lopsided amounts: whichever side is short decides how +/// much of the other side is actually taken. +/// +/// Accounts: +/// 0. `[]` pool +/// 1. `[]` pool authority +/// 2. `[signer]` depositor +/// 3. `[writable]` liquidity mint +/// 4. `[]` mint A +/// 5. `[]` mint B +/// 6. `[writable]` pool's token A account +/// 7. `[writable]` pool's token B account +/// 8. `[writable]` depositor's LP token account +/// 9. `[writable]` depositor's token A account +/// 10. `[writable]` depositor's token B account +/// 11. `[signer, writable]` payer +/// 12. `[]` system program +/// 13. `[]` SPL Token program +/// 14. `[]` associated token program +/// +/// Instruction data: `[amount_a: u64 (LE), amount_b: u64 (LE)]` +pub fn deposit_liquidity(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [pool, pool_authority, depositor, mint_liquidity, mint_a, mint_b, pool_account_a, pool_account_b, depositor_account_liquidity, depositor_account_a, depositor_account_b, payer, system_program, token_program, _associated_token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !depositor.is_signer() || !payer.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let requested_a = u64::from_le_bytes( + data.get(..8) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + let requested_b = u64::from_le_bytes( + data.get(8..16) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + + // Never take more than the depositor holds. + let mut amount_a = requested_a.min(token_amount(&depositor_account_a.try_borrow()?)?); + let mut amount_b = requested_b.min(token_amount(&depositor_account_b.try_borrow()?)?); + + let pool_a = token_amount(&pool_account_a.try_borrow()?)?; + let pool_b = token_amount(&pool_account_b.try_borrow()?)?; + let pool_creation = pool_a == 0 && pool_b == 0; + + if !pool_creation { + // Match the existing ratio, limited by whichever side is short. + let b_required = mul_div(amount_a, pool_b, pool_a)?; + if b_required <= amount_b { + amount_b = b_required; + } else { + amount_a = mul_div(amount_b, pool_a, pool_b)?; + } + } + + // Shares are the geometric mean of the deposit, so they track the pool's + // value rather than either side's balance. + let mut liquidity = + u64::try_from((amount_a as u128).checked_mul(amount_b as u128).ok_or(SwapError::MathOverflow)?.isqrt()) + .map_err(|_| SwapError::MathOverflow)?; + + if pool_creation { + // Burn a slice of the very first deposit permanently. Without it the + // pool could be emptied and its share price then trivially skewed. + if liquidity < MINIMUM_LIQUIDITY { + return Err(SwapError::DepositTooSmall.into()); + } + liquidity -= MINIMUM_LIQUIDITY; + } + + Transfer::<&AccountView> { + from: depositor_account_a, + to: pool_account_a, + authority: depositor, + amount: amount_a, + multisig_signers: &[], + } + .invoke()?; + Transfer::<&AccountView> { + from: depositor_account_b, + to: pool_account_b, + authority: depositor, + amount: amount_b, + multisig_signers: &[], + } + .invoke()?; + + CreateIdempotent { + funding_account: payer, + account: depositor_account_liquidity, + wallet: depositor, + mint: mint_liquidity, + system_program, + token_program, + } + .invoke()?; + + let bump = [seeds.authority_bump]; + let authority_seeds = seeds.authority_seeds(&bump); + + MintTo::<&AccountView> { + mint: mint_liquidity, + account: depositor_account_liquidity, + mint_authority: pool_authority, + amount: liquidity, + multisig_signers: &[], + } + .invoke_signed(&[Signer::from(&authority_seeds)])?; + + log!("Deposited liquidity"); + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/mod.rs b/tokens/token-swap/pinocchio/program/src/instructions/mod.rs new file mode 100644 index 000000000..f0ecb51f5 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/mod.rs @@ -0,0 +1,104 @@ +mod create_amm; +mod create_pool; +mod deposit_liquidity; +mod swap_exact_tokens_for_tokens; +mod withdraw_liquidity; + +pub use create_amm::*; +pub use create_pool::*; +pub use deposit_liquidity::*; +pub use swap_exact_tokens_for_tokens::*; +pub use withdraw_liquidity::*; + +use pinocchio::{cpi::Seed, AccountView, Address}; + +use crate::{ + error::SwapError, + state::{read_pool, AUTHORITY_SEED}, +}; + +/// The pool's PDAs, all seeded from the same three addresses. +pub struct PoolSeeds { + pub amm: [u8; 32], + pub mint_a: [u8; 32], + pub mint_b: [u8; 32], + pub authority_bump: u8, +} + +impl PoolSeeds { + /// Reads the pool account and rederives the authority, checking both the + /// pool and the authority accounts the caller supplied. + /// + /// The pool records which mints it is for, so binding the mints to the pool + /// here is what stops a caller pairing a real pool with someone else's + /// token accounts. + pub fn load( + program_id: &Address, + pool: &AccountView, + pool_authority: &AccountView, + mint_a: &AccountView, + mint_b: &AccountView, + ) -> Result { + if !pool.owned_by(program_id) { + return Err(SwapError::InvalidAccountData.into()); + } + + let stored = read_pool(&pool.try_borrow()?)?; + + if stored.mint_a != mint_a.address().as_ref() || stored.mint_b != mint_b.address().as_ref() { + return Err(SwapError::InvalidMint.into()); + } + let amm = stored.amm; + + let (pool_address, _) = + Address::find_program_address(&[&amm, mint_a.address().as_ref(), mint_b.address().as_ref()], program_id); + if pool.address() != &pool_address { + return Err(SwapError::InvalidSeeds.into()); + } + + let (authority_address, authority_bump) = Address::find_program_address( + &[&amm, mint_a.address().as_ref(), mint_b.address().as_ref(), AUTHORITY_SEED], + program_id, + ); + if pool_authority.address() != &authority_address { + return Err(SwapError::InvalidSeeds.into()); + } + + Ok(Self { amm, mint_a: stored.mint_a, mint_b: stored.mint_b, authority_bump }) + } + + /// The signer seeds for the pool authority, which owns the pool's token + /// accounts and the liquidity mint. + pub fn authority_seeds<'a>(&'a self, bump: &'a [u8; 1]) -> [Seed<'a>; 5] { + [ + Seed::from(&self.amm), + Seed::from(&self.mint_a), + Seed::from(&self.mint_b), + Seed::from(AUTHORITY_SEED), + Seed::from(bump), + ] + } +} + +/// Confirms `account` is the PDA for `seeds`, returning its bump. +pub fn expect_pda( + program_id: &Address, + account: &AccountView, + seeds: &[&[u8]], +) -> Result { + let (address, bump) = Address::find_program_address(seeds, program_id); + if account.address() != &address { + return Err(SwapError::InvalidSeeds.into()); + } + Ok(bump) +} + +/// `a * b / c` in `u128`, so the product of two `u64` amounts cannot overflow. +pub fn mul_div(a: u64, b: u64, c: u64) -> Result { + let result = (a as u128) + .checked_mul(b as u128) + .ok_or(SwapError::MathOverflow)? + .checked_div(c as u128) + .ok_or(SwapError::MathOverflow)?; + u64::try_from(result).map_err(|_| SwapError::MathOverflow.into()) +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs b/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs new file mode 100644 index 000000000..6847c78aa --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs @@ -0,0 +1,151 @@ +use pinocchio::{cpi::Signer, error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_log::log; +use pinocchio_token::instructions::Transfer; + +use crate::{ + error::SwapError, + instructions::{expect_pda, mul_div, PoolSeeds}, + state::{read_amm, token_amount, MAX_FEE_BASIS_POINTS}, +}; + +/// Trades an exact input amount for as much of the other side as the curve +/// gives, refusing anything below `min_output_amount`. +/// +/// Accounts: +/// 0. `[]` amm +/// 1. `[]` pool +/// 2. `[]` pool authority +/// 3. `[signer]` trader +/// 4. `[]` mint A +/// 5. `[]` mint B +/// 6. `[writable]` pool's token A account +/// 7. `[writable]` pool's token B account +/// 8. `[writable]` trader's token A account +/// 9. `[writable]` trader's token B account +/// 10. `[]` SPL Token program +/// +/// Instruction data: `[swap_a: u8, input_amount: u64 (LE), min_output_amount: u64 (LE)]` +/// where a non-zero `swap_a` means "pay in A, receive B". +pub fn swap_exact_tokens_for_tokens(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [amm, pool, pool_authority, trader, mint_a, mint_b, pool_account_a, pool_account_b, trader_account_a, trader_account_b, _token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !trader.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + if !amm.owned_by(program_id) { + return Err(SwapError::InvalidAccountData.into()); + } + + let swap_a = *data.first().ok_or(ProgramError::InvalidInstructionData)? != 0; + let input_amount = u64::from_le_bytes( + data.get(1..9) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + let min_output_amount = u64::from_le_bytes( + data.get(9..17) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + + // The pool records which AMM it belongs to, so the fee cannot be swapped + // for a cheaper one by passing a different AMM account. + let config = read_amm(&amm.try_borrow()?)?; + let fee = config.fee; + expect_pda(program_id, amm, &[&config.id])?; + if amm.address().as_ref() != seeds.amm { + return Err(SwapError::InvalidSeeds.into()); + } + if fee >= MAX_FEE_BASIS_POINTS { + return Err(SwapError::InvalidFee.into()); + } + + // Never take more than the trader holds. + let held = if swap_a { + token_amount(&trader_account_a.try_borrow()?)? + } else { + token_amount(&trader_account_b.try_borrow()?)? + }; + let input = input_amount.min(held); + + let fee_amount = mul_div(input, fee as u64, MAX_FEE_BASIS_POINTS as u64)?; + let taxed_input = input.checked_sub(fee_amount).ok_or(SwapError::MathOverflow)?; + + let pool_a = token_amount(&pool_account_a.try_borrow()?)?; + let pool_b = token_amount(&pool_account_b.try_borrow()?)?; + + // Constant product: the output is what keeps `a * b` from falling once the + // taxed input is added to the paying side. + let output = if swap_a { + mul_div(taxed_input, pool_b, pool_a.checked_add(taxed_input).ok_or(SwapError::MathOverflow)?)? + } else { + mul_div(taxed_input, pool_a, pool_b.checked_add(taxed_input).ok_or(SwapError::MathOverflow)?)? + }; + + if output < min_output_amount { + return Err(SwapError::OutputTooSmall.into()); + } + + let invariant = (pool_a as u128).checked_mul(pool_b as u128).ok_or(SwapError::MathOverflow)?; + + let bump = [seeds.authority_bump]; + let authority_seeds = seeds.authority_seeds(&bump); + let signer = [Signer::from(&authority_seeds)]; + + if swap_a { + Transfer::<&AccountView> { + from: trader_account_a, + to: pool_account_a, + authority: trader, + amount: input, + multisig_signers: &[], + } + .invoke()?; + Transfer::<&AccountView> { + from: pool_account_b, + to: trader_account_b, + authority: pool_authority, + amount: output, + multisig_signers: &[], + } + .invoke_signed(&signer)?; + } else { + Transfer::<&AccountView> { + from: trader_account_b, + to: pool_account_b, + authority: trader, + amount: input, + multisig_signers: &[], + } + .invoke()?; + Transfer::<&AccountView> { + from: pool_account_a, + to: trader_account_a, + authority: pool_authority, + amount: output, + multisig_signers: &[], + } + .invoke_signed(&signer)?; + } + + log!("Traded {} in for {} out", input, output); + + // Re-read after the CPIs. A higher invariant is fine — that is rounding in + // the pool's favour — but it must never fall. + let new_a = token_amount(&pool_account_a.try_borrow()?)?; + let new_b = token_amount(&pool_account_b.try_borrow()?)?; + let new_invariant = (new_a as u128).checked_mul(new_b as u128).ok_or(SwapError::MathOverflow)?; + if invariant > new_invariant { + return Err(SwapError::InvariantViolated.into()); + } + + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs b/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs new file mode 100644 index 000000000..2042cbb34 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs @@ -0,0 +1,91 @@ +use pinocchio::{cpi::Signer, error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_log::log; +use pinocchio_token::instructions::{Burn, Transfer}; + +use crate::{ + error::SwapError, + instructions::{mul_div, PoolSeeds}, + state::{mint_supply, token_amount, MINIMUM_LIQUIDITY}, +}; + +/// Burns LP tokens and returns the depositor's share of both sides. +/// +/// The share is measured against `supply + MINIMUM_LIQUIDITY`, because the +/// locked minimum was never minted to anyone but still backs the pool. +/// +/// Accounts: +/// 0. `[]` pool +/// 1. `[]` pool authority +/// 2. `[signer]` depositor +/// 3. `[writable]` liquidity mint +/// 4. `[]` mint A +/// 5. `[]` mint B +/// 6. `[writable]` pool's token A account +/// 7. `[writable]` pool's token B account +/// 8. `[writable]` depositor's LP token account +/// 9. `[writable]` depositor's token A account +/// 10. `[writable]` depositor's token B account +/// 11. `[]` SPL Token program +/// +/// Instruction data: `[amount: u64 (LE)]` +pub fn withdraw_liquidity(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [pool, pool_authority, depositor, mint_liquidity, mint_a, mint_b, pool_account_a, pool_account_b, depositor_account_liquidity, depositor_account_a, depositor_account_b, _token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !depositor.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let amount = u64::from_le_bytes( + data.get(..8) + .ok_or(ProgramError::InvalidInstructionData)? + .try_into() + .map_err(|_| ProgramError::InvalidInstructionData)?, + ); + + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + + let supply = mint_supply(&mint_liquidity.try_borrow()?)?; + let denominator = supply.checked_add(MINIMUM_LIQUIDITY).ok_or(SwapError::MathOverflow)?; + + let amount_a = mul_div(amount, token_amount(&pool_account_a.try_borrow()?)?, denominator)?; + let amount_b = mul_div(amount, token_amount(&pool_account_b.try_borrow()?)?, denominator)?; + + let bump = [seeds.authority_bump]; + let authority_seeds = seeds.authority_seeds(&bump); + let signer = [Signer::from(&authority_seeds)]; + + Transfer::<&AccountView> { + from: pool_account_a, + to: depositor_account_a, + authority: pool_authority, + amount: amount_a, + multisig_signers: &[], + } + .invoke_signed(&signer)?; + Transfer::<&AccountView> { + from: pool_account_b, + to: depositor_account_b, + authority: pool_authority, + amount: amount_b, + multisig_signers: &[], + } + .invoke_signed(&signer)?; + + // Burning last means an over-large `amount` fails here and rolls back the + // transfers above, so the pool cannot be drained by asking for too much. + Burn::<&AccountView> { + mint: mint_liquidity, + account: depositor_account_liquidity, + authority: depositor, + amount, + multisig_signers: &[], + } + .invoke()?; + + log!("Withdrew liquidity"); + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/program/src/lib.rs b/tokens/token-swap/pinocchio/program/src/lib.rs new file mode 100644 index 000000000..ec4e3bb09 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/lib.rs @@ -0,0 +1,12 @@ +#![no_std] + +pub mod error; +pub mod instructions; +pub mod processor; +pub mod state; +pub mod util; + +use pinocchio::{entrypoint, nostd_panic_handler}; + +entrypoint!(processor::process_instruction); +nostd_panic_handler!(); diff --git a/tokens/token-swap/pinocchio/program/src/processor.rs b/tokens/token-swap/pinocchio/program/src/processor.rs new file mode 100644 index 000000000..c23797e58 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/processor.rs @@ -0,0 +1,43 @@ +use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_log::log; + +use crate::instructions::{ + create_amm, create_pool, deposit_liquidity, swap_exact_tokens_for_tokens, withdraw_liquidity, +}; + +const CREATE_AMM: u8 = 0; +const CREATE_POOL: u8 = 1; +const DEPOSIT_LIQUIDITY: u8 = 2; +const WITHDRAW_LIQUIDITY: u8 = 3; +const SWAP_EXACT_TOKENS_FOR_TOKENS: u8 = 4; + +/// Entrypoint for the program. +pub fn process_instruction( + program_id: &Address, + accounts: &mut [AccountView], + instruction_data: &[u8], +) -> ProgramResult { + match instruction_data { + [CREATE_AMM, data @ ..] => { + log!("Instruction: CreateAmm"); + create_amm(program_id, accounts, data) + } + [CREATE_POOL, ..] => { + log!("Instruction: CreatePool"); + create_pool(program_id, accounts) + } + [DEPOSIT_LIQUIDITY, data @ ..] => { + log!("Instruction: DepositLiquidity"); + deposit_liquidity(program_id, accounts, data) + } + [WITHDRAW_LIQUIDITY, data @ ..] => { + log!("Instruction: WithdrawLiquidity"); + withdraw_liquidity(program_id, accounts, data) + } + [SWAP_EXACT_TOKENS_FOR_TOKENS, data @ ..] => { + log!("Instruction: SwapExactTokensForTokens"); + swap_exact_tokens_for_tokens(program_id, accounts, data) + } + _ => Err(ProgramError::InvalidInstructionData), + } +} diff --git a/tokens/token-swap/pinocchio/program/src/state.rs b/tokens/token-swap/pinocchio/program/src/state.rs new file mode 100644 index 000000000..f0f56e532 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/state.rs @@ -0,0 +1,100 @@ +//! Account layouts and the seeds that place them. +//! +//! Read and written as plain little-endian bytes. There is no 8-byte Anchor +//! account discriminator, so both accounts are 8 bytes smaller than their +//! counterparts in the Anchor version. + +use pinocchio::{error::ProgramError, Address}; + +use crate::error::SwapError; + +/// Seed suffix of the pool authority PDA. +pub const AUTHORITY_SEED: &[u8] = b"authority"; + +/// Seed suffix of the liquidity mint PDA. +pub const LIQUIDITY_SEED: &[u8] = b"liquidity"; + +/// Liquidity permanently locked on the first deposit. +/// +/// It is minted to nobody, which keeps the pool from ever being fully drained +/// and stops the share price being skewed while the pool is nearly empty. +pub const MINIMUM_LIQUIDITY: u64 = 100; + +/// The liquidity mint always has six decimals. +pub const LIQUIDITY_DECIMALS: u8 = 6; + +/// Fees are basis points, so anything at or above this is not a fee. +pub const MAX_FEE_BASIS_POINTS: u16 = 10_000; + +/// `id(32) | admin(32) | fee(2)` +pub const AMM_SIZE: usize = 66; + +/// `amm(32) | mint_a(32) | mint_b(32)` +pub const POOL_SIZE: usize = 96; + +/// Size of a legacy SPL Token mint. +pub const MINT_SIZE: usize = 82; + +pub struct AmmData { + pub id: [u8; 32], + pub fee: u16, +} + +pub fn read_amm(data: &[u8]) -> Result { + if data.len() != AMM_SIZE { + return Err(SwapError::InvalidAccountData.into()); + } + let mut id = [0u8; 32]; + id.copy_from_slice(&data[..32]); + Ok(AmmData { id, fee: u16::from_le_bytes(data[64..66].try_into().unwrap()) }) +} + +pub fn write_amm(data: &mut [u8], id: &[u8; 32], admin: &Address, fee: u16) -> Result<(), ProgramError> { + if data.len() != AMM_SIZE { + return Err(SwapError::InvalidAccountData.into()); + } + data[..32].copy_from_slice(id); + data[32..64].copy_from_slice(admin.as_ref()); + data[64..66].copy_from_slice(&fee.to_le_bytes()); + Ok(()) +} + +pub struct PoolData { + pub amm: [u8; 32], + pub mint_a: [u8; 32], + pub mint_b: [u8; 32], +} + +pub fn read_pool(data: &[u8]) -> Result { + if data.len() != POOL_SIZE { + return Err(SwapError::InvalidAccountData.into()); + } + let mut pool = PoolData { amm: [0u8; 32], mint_a: [0u8; 32], mint_b: [0u8; 32] }; + pool.amm.copy_from_slice(&data[..32]); + pool.mint_a.copy_from_slice(&data[32..64]); + pool.mint_b.copy_from_slice(&data[64..96]); + Ok(pool) +} + +pub fn write_pool(data: &mut [u8], amm: &Address, mint_a: &Address, mint_b: &Address) -> Result<(), ProgramError> { + if data.len() != POOL_SIZE { + return Err(SwapError::InvalidAccountData.into()); + } + data[..32].copy_from_slice(amm.as_ref()); + data[32..64].copy_from_slice(mint_a.as_ref()); + data[64..96].copy_from_slice(mint_b.as_ref()); + Ok(()) +} + +/// Reads an SPL token account's `amount` (offset 64) without deserializing the +/// whole account. +pub fn token_amount(data: &[u8]) -> Result { + let bytes = data.get(64..72).ok_or(SwapError::InvalidAccountData)?; + Ok(u64::from_le_bytes(bytes.try_into().unwrap())) +} + +/// Reads an SPL mint's `supply` (offset 36). +pub fn mint_supply(data: &[u8]) -> Result { + let bytes = data.get(36..44).ok_or(SwapError::InvalidAccountData)?; + Ok(u64::from_le_bytes(bytes.try_into().unwrap())) +} diff --git a/tokens/token-swap/pinocchio/program/src/util.rs b/tokens/token-swap/pinocchio/program/src/util.rs new file mode 100644 index 000000000..e4e771973 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/util.rs @@ -0,0 +1,38 @@ +//! Shared account-creation helper. + +use pinocchio::{ + cpi::{Seed, Signer}, + sysvars::{rent::Rent, Sysvar}, + AccountView, Address, ProgramResult, +}; +use pinocchio_system::instructions::{Allocate, Assign, Transfer}; + +/// Creates `account` at a PDA, tolerating an address that already holds +/// lamports. +/// +/// `CreateAccount` fails outright if the target has a balance, and every PDA +/// here has a publicly derivable address — so anyone could send one lamport to +/// a receipt or state address and permanently block the instruction that was +/// meant to create it. Topping the account up and then allocating and assigning +/// it separately sidesteps that; it is the same fallback Anchor's `init` +/// performs. +pub fn create_pda_account( + payer: &AccountView, + account: &mut AccountView, + space: usize, + owner: &Address, + seeds: &[Seed], +) -> ProgramResult { + let required = Rent::get()?.try_minimum_balance(space)?; + let current = account.lamports(); + + if current < required { + Transfer { from: payer, to: account, lamports: required - current }.invoke()?; + } + + let signer = [Signer::from(seeds)]; + Allocate { account, space: space as u64 }.invoke_signed(&signer)?; + Assign { account, owner }.invoke_signed(&signer)?; + + Ok(()) +} diff --git a/tokens/token-swap/pinocchio/tests/test.ts b/tokens/token-swap/pinocchio/tests/test.ts new file mode 100644 index 000000000..02ff2091b --- /dev/null +++ b/tokens/token-swap/pinocchio/tests/test.ts @@ -0,0 +1,441 @@ +import * as path from 'node:path'; +import { + AccountRole, + type Address, + type KeyPairSigner, + appendTransactionMessageInstruction, + appendTransactionMessageInstructions, + createTransactionMessage, + generateKeyPairSigner, + getAddressEncoder, + getProgramDerivedAddress, + lamports, + pipe, + setTransactionMessageFeePayerSigner, + signTransactionMessageWithSigners, +} from '@solana/kit'; +import { SYSTEM_PROGRAM_ADDRESS, getCreateAccountInstruction } from '@solana-program/system'; +import { + ASSOCIATED_TOKEN_PROGRAM_ADDRESS, + TOKEN_PROGRAM_ADDRESS, + findAssociatedTokenPda, + getCreateAssociatedTokenInstruction, + getInitializeMint2Instruction, + getMintToInstruction, + getTokenDecoder, +} from '@solana-program/token-2022'; +import { assert } from 'chai'; +import { FailedTransactionMetadata, LiteSVM } from 'litesvm'; + +const CREATE_AMM = 0; +const CREATE_POOL = 1; +const DEPOSIT_LIQUIDITY = 2; +const WITHDRAW_LIQUIDITY = 3; +const SWAP = 4; + +const MINT_SIZE = 82n; +const DECIMALS = 6; +const FEE_BASIS_POINTS = 500; // 5% +const MINIMUM_LIQUIDITY = 100n; +const FUNDED = 1_000_000n; +const DEPOSIT_A = 100_000n; +const DEPOSIT_B = 400_000n; + +const PROGRAM_SO = path.join(process.cwd(), 'tests', 'fixtures', 'token_swap_pinocchio_program.so'); +const addressEncoder = getAddressEncoder(); + +function u16le(n: number): Uint8Array { + return Uint8Array.of(n & 0xff, (n >> 8) & 0xff); +} +function u64(n: bigint): Uint8Array { + const b = new Uint8Array(8); + new DataView(b.buffer).setBigUint64(0, n, true); + return b; +} +function concatBytes(...parts: Uint8Array[]): Uint8Array { + const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0)); + let offset = 0; + for (const p of parts) { + out.set(p, offset); + offset += p.length; + } + return out; +} +function isqrt(value: bigint): bigint { + if (value < 2n) return value; + let x = value; + let y = (x + 1n) / 2n; + while (y < x) { + x = y; + y = (x + value / x) / 2n; + } + return x; +} + +describe('Token Swap (Pinocchio)', () => { + let svm: LiteSVM; + let programId: Address; + let payer: KeyPairSigner; + let ammId: Uint8Array; + let amm: Address; + let mintA: KeyPairSigner; + let mintB: KeyPairSigner; + let pool: Address; + let poolAuthority: Address; + let mintLiquidity: Address; + let poolAccountA: Address; + let poolAccountB: Address; + let userA: Address; + let userB: Address; + let userLiquidity: Address; + + before(async () => { + svm = new LiteSVM(); + // The program derives every PDA from the id it is invoked with, so a + // generated id keeps the test self-contained. + programId = (await generateKeyPairSigner()).address; + svm.addProgramFromFile(programId, PROGRAM_SO); + + payer = await generateKeyPairSigner(); + svm.airdrop(payer.address, lamports(100_000_000_000n)); + + // Mint A must sort before mint B only in the caller's convention; the + // program takes them in the order given. + mintA = await generateKeyPairSigner(); + mintB = await generateKeyPairSigner(); + + ammId = new Uint8Array(addressEncoder.encode((await generateKeyPairSigner()).address)); + [amm] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ammId] }); + }); + + async function derivePoolAddresses() { + const a = addressEncoder.encode(mintA.address); + const b = addressEncoder.encode(mintB.address); + const ammBytes = addressEncoder.encode(amm); + [pool] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ammBytes, a, b] }); + [poolAuthority] = await getProgramDerivedAddress({ + programAddress: programId, + seeds: [ammBytes, a, b, 'authority'], + }); + [mintLiquidity] = await getProgramDerivedAddress({ + programAddress: programId, + seeds: [ammBytes, a, b, 'liquidity'], + }); + [poolAccountA] = await findAssociatedTokenPda({ + owner: poolAuthority, + mint: mintA.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + [poolAccountB] = await findAssociatedTokenPda({ + owner: poolAuthority, + mint: mintB.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + [userLiquidity] = await findAssociatedTokenPda({ + owner: payer.address, + mint: mintLiquidity, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + } + + async function tx(instructions: Parameters[0][]) { + return signTransactionMessageWithSigners( + pipe( + createTransactionMessage({ version: 0 }), + m => setTransactionMessageFeePayerSigner(payer, m), + m => svm.setTransactionMessageLifetimeUsingLatestBlockhash(m), + m => appendTransactionMessageInstructions(instructions, m), + ), + ); + } + + function send(signedTx: Parameters[0], label: string) { + const result = svm.sendTransaction(signedTx); + if (result instanceof FailedTransactionMetadata) { + throw new Error(`${label} failed: ${result.err()}`); + } + return result; + } + + function tokenAmount(account: Address): bigint { + const acc = svm.getAccount(account); + if (!acc?.exists) throw new Error(`token account ${account} not found`); + return getTokenDecoder().decode(acc.data).amount; + } + + function createAmmIx(id: Uint8Array, ammAddress: Address, fee: number) { + return { + programAddress: programId, + accounts: [ + { address: ammAddress, role: AccountRole.WRITABLE }, + { address: payer.address, role: AccountRole.READONLY }, + { address: payer.address, role: AccountRole.WRITABLE_SIGNER, signer: payer }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(CREATE_AMM), id, u16le(fee)), + }; + } + + function depositIx(amountA: bigint, amountB: bigint) { + return { + programAddress: programId, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: poolAuthority, role: AccountRole.READONLY }, + { address: payer.address, role: AccountRole.WRITABLE_SIGNER, signer: payer }, + { address: mintLiquidity, role: AccountRole.WRITABLE }, + { address: mintA.address, role: AccountRole.READONLY }, + { address: mintB.address, role: AccountRole.READONLY }, + { address: poolAccountA, role: AccountRole.WRITABLE }, + { address: poolAccountB, role: AccountRole.WRITABLE }, + { address: userLiquidity, role: AccountRole.WRITABLE }, + { address: userA, role: AccountRole.WRITABLE }, + { address: userB, role: AccountRole.WRITABLE }, + { address: payer.address, role: AccountRole.WRITABLE_SIGNER, signer: payer }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(DEPOSIT_LIQUIDITY), u64(amountA), u64(amountB)), + }; + } + + function swapIx(swapA: boolean, input: bigint, minOutput: bigint) { + return { + programAddress: programId, + accounts: [ + { address: amm, role: AccountRole.READONLY }, + { address: pool, role: AccountRole.READONLY }, + { address: poolAuthority, role: AccountRole.READONLY }, + { address: payer.address, role: AccountRole.READONLY_SIGNER, signer: payer }, + { address: mintA.address, role: AccountRole.READONLY }, + { address: mintB.address, role: AccountRole.READONLY }, + { address: poolAccountA, role: AccountRole.WRITABLE }, + { address: poolAccountB, role: AccountRole.WRITABLE }, + { address: userA, role: AccountRole.WRITABLE }, + { address: userB, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(SWAP), Uint8Array.of(swapA ? 1 : 0), u64(input), u64(minOutput)), + }; + } + + it('Creates an AMM', async () => { + send(await tx([createAmmIx(ammId, amm, FEE_BASIS_POINTS)]), 'create amm'); + + const account = svm.getAccount(amm); + if (!account?.exists) throw new Error('amm not found'); + assert.equal(account.programAddress, programId, 'the AMM is owned by the program'); + assert.deepEqual(Array.from(account.data.slice(0, 32)), Array.from(ammId), 'the id was recorded'); + assert.equal(new DataView(account.data.buffer, account.data.byteOffset).getUint16(64, true), FEE_BASIS_POINTS); + }); + + it('Rejects a fee of 100% or more', async () => { + const id = new Uint8Array(addressEncoder.encode((await generateKeyPairSigner()).address)); + const [address] = await getProgramDerivedAddress({ programAddress: programId, seeds: [id] }); + + const result = svm.sendTransaction(await tx([createAmmIx(id, address, 10_000)])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the fee to be refused'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x0', + 'rejected with InvalidFee', + ); + }); + + it('Creates the mints and funds the depositor', async () => { + [userA] = await findAssociatedTokenPda({ + owner: payer.address, + mint: mintA.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + [userB] = await findAssociatedTokenPda({ + owner: payer.address, + mint: mintB.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + + for (const [mint, ata] of [ + [mintA, userA], + [mintB, userB], + ] as const) { + send( + await tx([ + getCreateAccountInstruction({ + payer, + newAccount: mint, + lamports: lamports(svm.minimumBalanceForRentExemption(MINT_SIZE)), + space: MINT_SIZE, + programAddress: TOKEN_PROGRAM_ADDRESS, + }), + getInitializeMint2Instruction( + { mint: mint.address, decimals: DECIMALS, mintAuthority: payer.address, freezeAuthority: null }, + { programAddress: TOKEN_PROGRAM_ADDRESS }, + ), + getCreateAssociatedTokenInstruction({ + payer, + ata, + owner: payer.address, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }), + getMintToInstruction( + { mint: mint.address, token: ata, mintAuthority: payer, amount: FUNDED }, + { programAddress: TOKEN_PROGRAM_ADDRESS }, + ), + ]), + 'create mint', + ); + } + + assert.equal(tokenAmount(userA), FUNDED); + assert.equal(tokenAmount(userB), FUNDED); + }); + + it('Creates the pool', async () => { + await derivePoolAddresses(); + + const ix = { + programAddress: programId, + accounts: [ + { address: amm, role: AccountRole.READONLY }, + { address: pool, role: AccountRole.WRITABLE }, + { address: poolAuthority, role: AccountRole.READONLY }, + { address: mintLiquidity, role: AccountRole.WRITABLE }, + { address: mintA.address, role: AccountRole.READONLY }, + { address: mintB.address, role: AccountRole.READONLY }, + { address: poolAccountA, role: AccountRole.WRITABLE }, + { address: poolAccountB, role: AccountRole.WRITABLE }, + { address: payer.address, role: AccountRole.WRITABLE_SIGNER, signer: payer }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: Uint8Array.of(CREATE_POOL), + }; + send(await tx([ix]), 'create pool'); + + const account = svm.getAccount(pool); + if (!account?.exists) throw new Error('pool not found'); + assert.deepEqual( + Array.from(account.data.slice(32, 64)), + Array.from(addressEncoder.encode(mintA.address)), + 'mint A recorded', + ); + assert.equal(tokenAmount(poolAccountA), 0n, 'vault A starts empty'); + assert.equal(tokenAmount(poolAccountB), 0n, 'vault B starts empty'); + }); + + it('Takes the first deposit and locks the minimum liquidity', async () => { + send(await tx([depositIx(DEPOSIT_A, DEPOSIT_B)]), 'deposit liquidity'); + + assert.equal(tokenAmount(poolAccountA), DEPOSIT_A, 'vault A funded'); + assert.equal(tokenAmount(poolAccountB), DEPOSIT_B, 'vault B funded'); + + // Shares are the geometric mean, less the permanently locked minimum. + const expected = isqrt(DEPOSIT_A * DEPOSIT_B) - MINIMUM_LIQUIDITY; + assert.equal(tokenAmount(userLiquidity), expected, 'LP tokens minted, minus the locked minimum'); + }); + + it('Swaps A for B along the constant product curve', async () => { + const input = 10_000n; + const poolABefore = tokenAmount(poolAccountA); + const poolBBefore = tokenAmount(poolAccountB); + const userBBefore = tokenAmount(userB); + + const taxed = input - (input * BigInt(FEE_BASIS_POINTS)) / 10_000n; + const expectedOutput = (taxed * poolBBefore) / (poolABefore + taxed); + + send(await tx([swapIx(true, input, 1n)]), 'swap a for b'); + + assert.equal(tokenAmount(userB) - userBBefore, expectedOutput, 'output matches the curve'); + assert.equal(tokenAmount(poolAccountA), poolABefore + input, 'the full input entered the pool'); + + // The fee stays in the pool, so the invariant strictly grows. + assert.isAbove( + Number(tokenAmount(poolAccountA) * tokenAmount(poolAccountB)), + Number(poolABefore * poolBBefore), + 'the invariant did not fall', + ); + }); + + it('Rejects a swap whose output would miss the minimum', async () => { + const result = svm.sendTransaction(await tx([swapIx(true, 1_000n, 1_000_000n)])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the slippage guard to fire'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x3', + 'rejected with OutputTooSmall', + ); + }); + + it('Trims a lopsided deposit to the pool ratio', async () => { + const poolABefore = tokenAmount(poolAccountA); + const poolBBefore = tokenAmount(poolAccountB); + const userABefore = tokenAmount(userA); + + // Offer far more B than the ratio needs; only the matching amount of B + // should be taken for the A supplied. + const offerA = 10_000n; + const expectedB = (offerA * poolBBefore) / poolABefore; + + send(await tx([depositIx(offerA, FUNDED)]), 'ratio deposit'); + + assert.equal(userABefore - tokenAmount(userA), offerA, 'all of the offered A was taken'); + assert.equal(tokenAmount(poolAccountB) - poolBBefore, expectedB, 'only the matching B was taken'); + }); + + it('Withdraws liquidity back to both sides', async () => { + const shares = tokenAmount(userLiquidity); + const userABefore = tokenAmount(userA); + const userBBefore = tokenAmount(userB); + + const withdrawIx = { + programAddress: programId, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: poolAuthority, role: AccountRole.READONLY }, + { address: payer.address, role: AccountRole.READONLY_SIGNER, signer: payer }, + { address: mintLiquidity, role: AccountRole.WRITABLE }, + { address: mintA.address, role: AccountRole.READONLY }, + { address: mintB.address, role: AccountRole.READONLY }, + { address: poolAccountA, role: AccountRole.WRITABLE }, + { address: poolAccountB, role: AccountRole.WRITABLE }, + { address: userLiquidity, role: AccountRole.WRITABLE }, + { address: userA, role: AccountRole.WRITABLE }, + { address: userB, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(WITHDRAW_LIQUIDITY), u64(shares)), + }; + send(await tx([withdrawIx]), 'withdraw liquidity'); + + assert.equal(tokenAmount(userLiquidity), 0n, 'all shares burned'); + assert.isAbove(Number(tokenAmount(userA) - userABefore), 0, 'token A returned'); + assert.isAbove(Number(tokenAmount(userB) - userBBefore), 0, 'token B returned'); + + // The locked minimum keeps the pool from being emptied entirely. + assert.isAbove(Number(tokenAmount(poolAccountA)), 0, 'the pool retains the locked share'); + assert.isAbove(Number(tokenAmount(poolAccountB)), 0, 'the pool retains the locked share'); + }); + + it('Rejects a pool paired with the wrong mints', async () => { + // The pool records its own mints, so passing someone else's mint must + // not let a caller point a real pool at unrelated token accounts. + const otherMint = await generateKeyPairSigner(); + const ix = { + ...swapIx(true, 1_000n, 1n), + accounts: swapIx(true, 1_000n, 1n).accounts.map((account, index) => + index === 4 ? { address: otherMint.address, role: AccountRole.READONLY } : account, + ), + }; + + const result = svm.sendTransaction(await tx([ix])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the mint swap to be refused'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x1', + 'rejected with InvalidMint', + ); + }); +}); diff --git a/tokens/token-swap/pinocchio/tsconfig.json b/tokens/token-swap/pinocchio/tsconfig.json new file mode 100644 index 000000000..c02443141 --- /dev/null +++ b/tokens/token-swap/pinocchio/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai", "node"], + "typeRoots": ["./node_modules/@types"], + "lib": ["esnext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true + } +} From 3b7676987c871247d6b43739a6570a09ca430879 Mon Sep 17 00:00:00 2001 From: MarkFeder Date: Mon, 31 Aug 2026 16:20:01 +0200 Subject: [PATCH 2/2] token-swap: bind the pool vaults and liquidity mint to the pool PoolSeeds::load rederived the pool and its authority but never checked the two vaults, and neither deposit nor withdraw checked the liquidity mint. Anchor gets those from associated_token::mint/authority and the mint's seeds constraint; dropping them in the port left three ways to drain a pool: - swap with a zero-balance stand-in for the paying reserve prices the trade against nothing and empties the opposite vault; - deposit into caller-controlled reserves still mints genuine LP shares, redeemable against the real ones; - withdraw against a caller-created LP mint sets the entitlement to whatever they like. Rederive both vaults as the authority's associated token accounts, and the liquidity mint from its seeds. Each vector has a test verified to succeed without the checks. --- .../src/instructions/deposit_liquidity.rs | 3 +- .../pinocchio/program/src/instructions/mod.rs | 63 +++++++- .../swap_exact_tokens_for_tokens.rs | 2 +- .../src/instructions/withdraw_liquidity.rs | 3 +- tokens/token-swap/pinocchio/tests/test.ts | 142 ++++++++++++++++++ 5 files changed, 204 insertions(+), 9 deletions(-) diff --git a/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs b/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs index 52423b135..c7edffa6e 100644 --- a/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs +++ b/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs @@ -57,7 +57,8 @@ pub fn deposit_liquidity(program_id: &Address, accounts: &mut [AccountView], dat .map_err(|_| ProgramError::InvalidInstructionData)?, ); - let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b, pool_account_a, pool_account_b)?; + seeds.check_liquidity_mint(program_id, mint_liquidity)?; // Never take more than the depositor holds. let mut amount_a = requested_a.min(token_amount(&depositor_account_a.try_borrow()?)?); diff --git a/tokens/token-swap/pinocchio/program/src/instructions/mod.rs b/tokens/token-swap/pinocchio/program/src/instructions/mod.rs index f0ecb51f5..8451eec2b 100644 --- a/tokens/token-swap/pinocchio/program/src/instructions/mod.rs +++ b/tokens/token-swap/pinocchio/program/src/instructions/mod.rs @@ -14,9 +14,29 @@ use pinocchio::{cpi::Seed, AccountView, Address}; use crate::{ error::SwapError, - state::{read_pool, AUTHORITY_SEED}, + state::{read_pool, AUTHORITY_SEED, LIQUIDITY_SEED}, }; +/// The legacy SPL Token program ID (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`). +pub const SPL_TOKEN_PROGRAM_ID: Address = + pinocchio::Address::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + +/// The associated token program ID (`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`). +pub const ASSOCIATED_TOKEN_PROGRAM_ID: Address = + pinocchio::Address::from_str_const("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); + +/// Derives `owner`'s associated token account for `mint`. +/// +/// The associated token program seeds its accounts `[owner, token program, +/// mint]`, so this reproduces the one canonical address for that pair. +pub fn associated_token_address(owner: &Address, mint: &Address) -> Address { + let (address, _) = Address::find_program_address( + &[owner.as_ref(), SPL_TOKEN_PROGRAM_ID.as_ref(), mint.as_ref()], + &ASSOCIATED_TOKEN_PROGRAM_ID, + ); + address +} + /// The pool's PDAs, all seeded from the same three addresses. pub struct PoolSeeds { pub amm: [u8; 32], @@ -26,18 +46,23 @@ pub struct PoolSeeds { } impl PoolSeeds { - /// Reads the pool account and rederives the authority, checking both the - /// pool and the authority accounts the caller supplied. + /// Reads the pool account and rederives everything the pool owns, checking + /// each account the caller supplied against the derivation. /// - /// The pool records which mints it is for, so binding the mints to the pool - /// here is what stops a caller pairing a real pool with someone else's - /// token accounts. + /// The pool records which mints it is for, and both vaults are the + /// authority's associated token accounts for those mints. Rederiving them + /// is what the Anchor version gets from its `associated_token::mint` and + /// `associated_token::authority` constraints — without it a caller can hand + /// over a vault of their own choosing, which is enough to misprice a trade + /// or mint LP shares against reserves the pool never received. pub fn load( program_id: &Address, pool: &AccountView, pool_authority: &AccountView, mint_a: &AccountView, mint_b: &AccountView, + pool_account_a: &AccountView, + pool_account_b: &AccountView, ) -> Result { if !pool.owned_by(program_id) { return Err(SwapError::InvalidAccountData.into()); @@ -64,9 +89,35 @@ impl PoolSeeds { return Err(SwapError::InvalidSeeds.into()); } + // Both vaults are the authority's associated token accounts. Their + // balances price every trade and back every LP share, so a substituted + // one is worth more than a wrong mint. + if pool_account_a.address() != &associated_token_address(&authority_address, mint_a.address()) + || pool_account_b.address() != &associated_token_address(&authority_address, mint_b.address()) + { + return Err(SwapError::InvalidSeeds.into()); + } + Ok(Self { amm, mint_a: stored.mint_a, mint_b: stored.mint_b, authority_bump }) } + /// Confirms `mint_liquidity` is this pool's LP mint. + /// + /// Withdrawals divide by its supply and deposits mint from it, so an + /// attacker-supplied mint would let them set their own entitlement. + pub fn check_liquidity_mint( + &self, + program_id: &Address, + mint_liquidity: &AccountView, + ) -> Result<(), pinocchio::error::ProgramError> { + let (address, _) = + Address::find_program_address(&[&self.amm, &self.mint_a, &self.mint_b, LIQUIDITY_SEED], program_id); + if mint_liquidity.address() != &address { + return Err(SwapError::InvalidSeeds.into()); + } + Ok(()) + } + /// The signer seeds for the pool authority, which owns the pool's token /// accounts and the liquidity mint. pub fn authority_seeds<'a>(&'a self, bump: &'a [u8; 1]) -> [Seed<'a>; 5] { diff --git a/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs b/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs index 6847c78aa..4751ddf38 100644 --- a/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs +++ b/tokens/token-swap/pinocchio/program/src/instructions/swap_exact_tokens_for_tokens.rs @@ -54,7 +54,7 @@ pub fn swap_exact_tokens_for_tokens(program_id: &Address, accounts: &mut [Accoun .map_err(|_| ProgramError::InvalidInstructionData)?, ); - let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b, pool_account_a, pool_account_b)?; // The pool records which AMM it belongs to, so the fee cannot be swapped // for a cheaper one by passing a different AMM account. diff --git a/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs b/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs index 2042cbb34..3b4afc017 100644 --- a/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs +++ b/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs @@ -46,7 +46,8 @@ pub fn withdraw_liquidity(program_id: &Address, accounts: &mut [AccountView], da .map_err(|_| ProgramError::InvalidInstructionData)?, ); - let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b)?; + let seeds = PoolSeeds::load(program_id, pool, pool_authority, mint_a, mint_b, pool_account_a, pool_account_b)?; + seeds.check_liquidity_mint(program_id, mint_liquidity)?; let supply = mint_supply(&mint_liquidity.try_borrow()?)?; let denominator = supply.checked_add(MINIMUM_LIQUIDITY).ok_or(SwapError::MathOverflow)?; diff --git a/tokens/token-swap/pinocchio/tests/test.ts b/tokens/token-swap/pinocchio/tests/test.ts index 02ff2091b..ab7e00fe6 100644 --- a/tokens/token-swap/pinocchio/tests/test.ts +++ b/tokens/token-swap/pinocchio/tests/test.ts @@ -20,6 +20,7 @@ import { TOKEN_PROGRAM_ADDRESS, findAssociatedTokenPda, getCreateAssociatedTokenInstruction, + getInitializeAccount3Instruction, getInitializeMint2Instruction, getMintToInstruction, getTokenDecoder, @@ -419,6 +420,147 @@ describe('Token Swap (Pinocchio)', () => { assert.isAbove(Number(tokenAmount(poolAccountB)), 0, 'the pool retains the locked share'); }); + // A token account for `mint`, owned by the pool authority but NOT its + // associated token account. Anyone can create one of these, so nothing but + // an address check keeps it out of an instruction. + async function rogueVault(mint: Address): Promise
{ + const account = await generateKeyPairSigner(); + const size = 165n; + const createIx = getCreateAccountInstruction({ + payer, + newAccount: account, + lamports: lamports(svm.minimumBalanceForRentExemption(size)), + space: size, + programAddress: TOKEN_PROGRAM_ADDRESS, + }); + const initIx = getInitializeAccount3Instruction( + { account: account.address, mint, owner: poolAuthority }, + { programAddress: TOKEN_PROGRAM_ADDRESS }, + ); + send(await tx([createIx, initIx]), 'create rogue vault'); + return account.address; + } + + it('Rejects a swap routed through a substituted vault', async () => { + // An empty stand-in for the paying side would price the trade against a + // zero reserve and drain the genuine opposite vault. The vault is owned + // by the pool authority and holds the right mint, so only rederiving + // its address catches it. + const rogue = await rogueVault(mintA.address); + const base = swapIx(true, 1_000n, 1n); + const ix = { + ...base, + accounts: base.accounts.map((account, index) => + index === 6 ? { address: rogue, role: AccountRole.WRITABLE } : account, + ), + }; + + const poolBBefore = tokenAmount(poolAccountB); + const result = svm.sendTransaction(await tx([ix])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the substituted vault to be rejected'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x6', + 'rejected with InvalidSeeds', + ); + assert.equal(tokenAmount(poolAccountB), poolBBefore, 'the genuine vault was untouched'); + }); + + it('Rejects a deposit routed through a substituted vault', async () => { + // Otherwise the deposit lands in an account of the caller's choosing + // while the pool still mints them genuine LP shares. + const rogue = await rogueVault(mintA.address); + const base = depositIx(1_000n, 1_000n); + const ix = { + ...base, + accounts: base.accounts.map((account, index) => + index === 6 ? { address: rogue, role: AccountRole.WRITABLE } : account, + ), + }; + + const sharesBefore = tokenAmount(userLiquidity); + const result = svm.sendTransaction(await tx([ix])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the substituted vault to be rejected'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x6', + 'rejected with InvalidSeeds', + ); + assert.equal(tokenAmount(userLiquidity), sharesBefore, 'no shares were minted'); + }); + + it('Rejects a withdrawal against a counterfeit liquidity mint', async () => { + // The withdrawal entitlement is `amount / (supply + minimum)`, so a mint + // the caller controls lets them name their own share of the reserves. + const counterfeit = await generateKeyPairSigner(); + const [counterfeitAta] = await findAssociatedTokenPda({ + owner: payer.address, + mint: counterfeit.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + send( + await tx([ + getCreateAccountInstruction({ + payer, + newAccount: counterfeit, + lamports: lamports(svm.minimumBalanceForRentExemption(MINT_SIZE)), + space: MINT_SIZE, + programAddress: TOKEN_PROGRAM_ADDRESS, + }), + getInitializeMint2Instruction( + { + mint: counterfeit.address, + decimals: DECIMALS, + mintAuthority: payer.address, + freezeAuthority: null, + }, + { programAddress: TOKEN_PROGRAM_ADDRESS }, + ), + getCreateAssociatedTokenInstruction({ + payer, + ata: counterfeitAta, + owner: payer.address, + mint: counterfeit.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }), + getMintToInstruction( + { mint: counterfeit.address, token: counterfeitAta, mintAuthority: payer, amount: 1_000n }, + { programAddress: TOKEN_PROGRAM_ADDRESS }, + ), + ]), + 'create counterfeit liquidity mint', + ); + + const ix = { + programAddress: programId, + accounts: [ + { address: pool, role: AccountRole.READONLY }, + { address: poolAuthority, role: AccountRole.READONLY }, + { address: payer.address, role: AccountRole.READONLY_SIGNER, signer: payer }, + { address: counterfeit.address, role: AccountRole.WRITABLE }, + { address: mintA.address, role: AccountRole.READONLY }, + { address: mintB.address, role: AccountRole.READONLY }, + { address: poolAccountA, role: AccountRole.WRITABLE }, + { address: poolAccountB, role: AccountRole.WRITABLE }, + { address: counterfeitAta, role: AccountRole.WRITABLE }, + { address: userA, role: AccountRole.WRITABLE }, + { address: userB, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(WITHDRAW_LIQUIDITY), u64(1_000n)), + }; + + const poolABefore = tokenAmount(poolAccountA); + const result = svm.sendTransaction(await tx([ix])); + assert.instanceOf(result, FailedTransactionMetadata, 'expected the counterfeit mint to be rejected'); + assert.include( + (result as FailedTransactionMetadata).meta().logs().join('\n'), + 'custom program error: 0x6', + 'rejected with InvalidSeeds', + ); + assert.equal(tokenAmount(poolAccountA), poolABefore, 'the reserves were untouched'); + }); + it('Rejects a pool paired with the wrong mints', async () => { // The pool records its own mints, so passing someone else's mint must // not let a caller point a real pool at unrelated token accounts.