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..c7edffa6e --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/deposit_liquidity.rs @@ -0,0 +1,137 @@ +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, 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()?)?); + 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..8451eec2b --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/mod.rs @@ -0,0 +1,155 @@ +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, 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], + pub mint_a: [u8; 32], + pub mint_b: [u8; 32], + pub authority_bump: u8, +} + +impl PoolSeeds { + /// 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, 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()); + } + + 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()); + } + + // 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] { + [ + 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..4751ddf38 --- /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, 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. + 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..3b4afc017 --- /dev/null +++ b/tokens/token-swap/pinocchio/program/src/instructions/withdraw_liquidity.rs @@ -0,0 +1,92 @@ +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, 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)?; + + 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..ab7e00fe6 --- /dev/null +++ b/tokens/token-swap/pinocchio/tests/test.ts @@ -0,0 +1,583 @@ +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, + getInitializeAccount3Instruction, + 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'); + }); + + // 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. + 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 + } +}