From a3761b859c4ae744f38ad0f93d78342cd4655c5d Mon Sep 17 00:00:00 2001 From: MarkFeder <5670736+MarkFeder@users.noreply.github.com> Date: Sun, 30 Aug 2026 11:40:06 +0200 Subject: [PATCH 1/4] feat(token-fundraiser): add pinocchio example Ports the token-fundraiser example to Pinocchio (the anchor example has no native sibling). A maker starts a fundraiser with a token target and a duration; contributors deposit tokens into a PDA-owned vault up to a per-contributor cap while the fundraiser runs. Once the target is met the maker releases the funds; if the fundraiser ends without meeting the target, contributors can refund their deposits. Four instructions (initialize, contribute, check_contributions, refund) over two PDA state accounts, using pinocchio-token / pinocchio-associated-token- account for the vault and transfers, PDA-signed CPIs to move funds out of the vault, and the Clock sysvar for the time-based logic. The two inverted time checks in the anchor example are corrected here (contributions are only accepted while running; refunds only after the fundraiser ends). The litesvm test drives the full lifecycle: the refund path (contribute, warp the clock past the deadline, refund) and the release path (ten contributors reach the target, then the maker releases the funds), controlling the clock to exercise the time branches. --- Cargo.lock | 12 + Cargo.toml | 1 + tokens/token-fundraiser/pinocchio/cicd.sh | 8 + .../token-fundraiser/pinocchio/package.json | 24 + .../token-fundraiser/pinocchio/pnpm-lock.yaml | 2892 +++++++++++++++++ .../pinocchio/program/Cargo.toml | 22 + .../pinocchio/program/src/error.rs | 29 + .../src/instructions/check_contributions.rs | 110 + .../program/src/instructions/contribute.rs | 134 + .../program/src/instructions/initialize.rs | 94 + .../pinocchio/program/src/instructions/mod.rs | 57 + .../program/src/instructions/refund.rs | 103 + .../pinocchio/program/src/lib.rs | 11 + .../pinocchio/program/src/processor.rs | 39 + .../pinocchio/program/src/state.rs | 88 + .../token-fundraiser/pinocchio/tests/test.ts | 349 ++ .../token-fundraiser/pinocchio/tsconfig.json | 15 + 17 files changed, 3988 insertions(+) create mode 100644 tokens/token-fundraiser/pinocchio/cicd.sh create mode 100644 tokens/token-fundraiser/pinocchio/package.json create mode 100644 tokens/token-fundraiser/pinocchio/pnpm-lock.yaml create mode 100644 tokens/token-fundraiser/pinocchio/program/Cargo.toml create mode 100644 tokens/token-fundraiser/pinocchio/program/src/error.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/instructions/initialize.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/instructions/mod.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/lib.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/processor.rs create mode 100644 tokens/token-fundraiser/pinocchio/program/src/state.rs create mode 100644 tokens/token-fundraiser/pinocchio/tests/test.ts create mode 100644 tokens/token-fundraiser/pinocchio/tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 2a87face1..561e962ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1670,6 +1670,18 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fundraiser-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 = "funty" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index f703dde94..28e77cbf0 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-fundraiser/pinocchio/program", ] resolver = "2" diff --git a/tokens/token-fundraiser/pinocchio/cicd.sh b/tokens/token-fundraiser/pinocchio/cicd.sh new file mode 100644 index 000000000..e41db2140 --- /dev/null +++ b/tokens/token-fundraiser/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-fundraiser/pinocchio/package.json b/tokens/token-fundraiser/pinocchio/package.json new file mode 100644 index 000000000..dc8beffad --- /dev/null +++ b/tokens/token-fundraiser/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": "^0.15.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-fundraiser/pinocchio/pnpm-lock.yaml b/tokens/token-fundraiser/pinocchio/pnpm-lock.yaml new file mode 100644 index 000000000..1cbdeb21f --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/pnpm-lock.yaml @@ -0,0 +1,2892 @@ +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': + specifier: ^0.15.0 + version: 0.15.0(@solana/kit@7.1.1(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.12 + 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'} + + '@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.13.0': + resolution: {integrity: sha512-Id6QQxCG7ByImXPD5X/c3Ag2kySD+49aJ9waIkfxyUFyokhMQgxYQAx2rKuaygaBlaBQY6VVfBS46pqxZV+99A==} + peerDependencies: + '@solana/kit': ^7.0.0 + + '@solana-program/system@0.14.0': + resolution: {integrity: sha512-Pjs2RINZHYmk/pqWNBCuQmfNjZT9woPJ/w7QkzzCSwcqcokbARtxsnIdgj4lJVxvr1DuxG8JGIbKnq6z1QRY6Q==} + peerDependencies: + '@solana/kit': ^8.0.0 + + '@solana-program/token@0.15.0': + resolution: {integrity: sha512-SeLm08EYIfT453I6lo7wTGgfMbz1s7bJ1jSHFHBFebSJHD3xF46zRgMxq3YKRlr/RM8jN0cG8SjZVu+IDvMxEA==} + engines: {node: '>=24.0.0'} + peerDependencies: + '@solana/kit': ^7.0.0 + + '@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/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.1.0': + resolution: {integrity: sha512-28u6+aw2pc1pL9ZHtrHVLy/vzaORFcVDR+lZy24uy6Z2N/LLsVbaGOLihKD2ksNmu9y2xOn7deqsG9SSFXwKvg==} + 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.1.0': + resolution: {integrity: sha512-oqoiiNMjcf2mY1lnDE3SXDeifilUytNXxTK38DfSEbPYf+KSO7p+fsEcjC7/EuONo+swMIQfE9WtXdMRu3l/9g==} + 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.1.0': + resolution: {integrity: sha512-D3fFyyAFBVFoOICW8AtVMg7CK5+SdGcICHIQ/DJ/4cxypmsJOO8U9gaxU67/zMmuGAlnGNsUxdYCzDxoFC+iVg==} + 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.1.0': + resolution: {integrity: sha512-ClBbCrHbxgibtSZp3G+3S2PYFaw2Slx229KbFAXcZ52TAXgVRnXgNOY7HpURbmxnBFzEHSMu9u1zR7Ap8Tfk+g==} + 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.1.0': + resolution: {integrity: sha512-XmHx2kt5ni/mukLMHQRNobrzEIXKu9EmDqVO8qJU7NGkxtWYgUxETB7NfpoBXY2YSdWNfnuG+4FAi+V2cxcDKA==} + 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.1.0': + resolution: {integrity: sha512-3qVIu0Fqx5dgBri+bE65d02dA7wvGdg4t9t8PJl4vvdUu/d531YhqAIqTLMSGuC+TeemAZa5FRsT5okuvqrb/g==} + 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.1.0': + resolution: {integrity: sha512-Mhq97DN2y7KFTRuxrEEyXvVuYrPLn1VZKv0h8D0yvK0RMdWmEBRUC2kucc9cVlV53Z+RrS4ovGICtnf+ZkFoYw==} + 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.1.0': + resolution: {integrity: sha512-hjxbwWVjEcBVF7AWgsZWftPK/d3Xqj43ed9gV3X00zYNXFXvobEgZEjtu9NCOUb85LFUaRam99Mub/vqSlWrjQ==} + 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.1.0': + resolution: {integrity: sha512-dCuQR9nSVp4GBESLpOOIH84px5REySC4V5TMbR1PvkI09DU1BV8Pi/4W466enijO8NXCKwkL5YLNHdn97KBPDA==} + 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.1.0': + resolution: {integrity: sha512-BdiWGZRGwb4ExG/EQq9mAqhQI0weJZ3ikDQuI10IvOrhm1ukkZ2OhM0UXhKgRbnX3mjkmm6Iufyn9cTTZzWgRg==} + 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.1.0': + resolution: {integrity: sha512-WAHdYPKBk3hXvpYyJkUPzFTnpQAeGzDlIjskPXdCfPTT15o8+dSlN6nfPWz+EVZ33+8REkXyvuyQ7eW99j7iMg==} + 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.1.0': + resolution: {integrity: sha512-x1qTNhYttsiqyuhsWOJyVfZIrKlf666hi+P2+NnkxxDFUm3RTDS9czIUVUQ9gdnLpJ73dGkYZkfEIDRx6zTRdw==} + 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.1.0': + resolution: {integrity: sha512-GLSCWzIf080Uej6wQF/QIgSMFgJ3ZbUZ29jdUhcThypxyLlQnxIaD7iwPLwuPnW7jNN9q/4xTcpvHlfsvfXxRA==} + 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.1.0': + resolution: {integrity: sha512-uHq7EzR+0aGXH/lYiHS5QgDh8KWdJ7OiKzijqcYznTfpyM0bX15JtNhwL8BzMmQInlI2LOYwoe6tFCpP4GsDQw==} + 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.1.0': + resolution: {integrity: sha512-L6Sx0yY4PAjGuzHtzq8r20Yacvv48c16CXFeZV5Neci1SC7pJq1wRiOmdEtTzZ1Cghilfm6IQwMnRo1B4Q6ajw==} + 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.1.0': + resolution: {integrity: sha512-TLbHYh5SWe2e4xSYHhWT3oFub2guMVyJcGurUtQ0ij2u0lH80IkqeWUsDUWdroKF1AAYkXbZPuWdRO51IuY4gg==} + 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.1.0': + resolution: {integrity: sha512-FRdH8A1FU6glQohUZhGPoRREFDp5kDA98M5veCrL+MP2rm37Ws4JPY/yJZ0mpcu0kmEJTrT0x5wiNJtRUQbQhA==} + 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.1.0': + resolution: {integrity: sha512-IdpxHMlOuONS0vVxZViDRhhqaShG++hO4/wRlp5NMb/0WODSTTRTIIrDOmhiioTmOKJ9PIM6gAxtRys5/5uNHA==} + 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.1.0': + resolution: {integrity: sha512-8DcPRNlH91OHpxql22lLdagMI+FRb4MDKwPntvQhNAeLaGck70xVqmgR2E+oZj7w2whzRXpKRWy+uRLPhy3R/Q==} + 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.1.0': + resolution: {integrity: sha512-xrfErUO9WaUhxfmyMjVxoWHEVF1yxWQEAF491ur4CgDpfL8UnhV0tiWp1+6duOplgRkEIEnARuS83S4YZjSgOg==} + 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.1.0': + resolution: {integrity: sha512-4Ck5NnwCI/LKFlFvDzbycIXk38ZcHHup9SIcXs0ixmjngRRqvToXHoSJMeKcrmbc9jvg71IDVDav+ibm/68t3w==} + 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.1.0': + resolution: {integrity: sha512-R1qsD89GhW8xdfekk8ph7C2KK8aeYhhz5Z5nLJy9DZMDFFsWP7tj0tZW/sY0yFVqKsIsIBzdthiOC3npktNH4A==} + 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.1.0': + resolution: {integrity: sha512-wDRP/tZuqzjQAuX+Ce5ZbWlFrdtX5qZSC3VvKJAGqe4XaxHiREOCZ78+N+0IWJYsyvK1FEzzaJAGVQ6RsovMRQ==} + 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.1.0': + resolution: {integrity: sha512-tx904NuIXOi/SUqxBQb53yIa7/iYM9QdwUX96IPhnzJrkmnW28/k2rZ+4fTzIaaY/ATouoE9zUJH0vm5v6R08A==} + 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.1.0': + resolution: {integrity: sha512-vMhz0bKCol7DTDoevWLCKXmIUQqhDonB8660rT2SNuo6ETSm+pHmUdsbkif4fpOeBS/YKDcr47Sy8zDXLPiutQ==} + 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.1.0': + resolution: {integrity: sha512-3tPIXReVJQ6e4zZnMAkX/qSg35YeZ8n2OqbmXOolNj6tVeOUWY4jBM1XFOw8AZZ54qShAwMckBokyGiJoEx9Zg==} + 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.1.0': + resolution: {integrity: sha512-t/hkyeA2dMxQoRrFTfut5Mm6IJeQm/xaHXlmznivgfZFYfdVInQLwSnNsj3aaQRdTpuFnLDFViM+1VtqDAV/qw==} + 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.1.0': + resolution: {integrity: sha512-IduXbEBtRtHP+UWe5FJZwfX24o7aiw1IuIDhDktfOduo26Meo7rml+qvxusSehaRK7lLXHAl52L9np5K91MveQ==} + 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.1.0': + resolution: {integrity: sha512-P5kdtzM48/quxGy+IWkcpDtX7+akFafb+ckBORdKFnXntuwtjI+bMvT86rjMjtsBIza0ytdKgE786tQtrLQxdA==} + 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.1.0': + resolution: {integrity: sha512-LI7kxSez9ev0p5w5oQQW+Z//Niu2wRkoiGIeC5C0DhBwYbaCNyXGxYYCrGbJfTYJyaa9H/BpXUOncQSeDpLoYQ==} + 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.1.0': + resolution: {integrity: sha512-VqLBOXFqxeyGMcOuJBX8EKdksy9dooZz74oU7w6fG636W+Du2oFsp/hVd2rsXbCK/8PVAthP3DkKjDkMSjNMow==} + 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.1.0': + resolution: {integrity: sha512-GiFe3oFuzZs3S/oeVDumzQc/+3lCc3k/9/T43Bhdi8f9ctsAN2S9X5tADAgh6ZngP9p0BT5yecNEBny+jL/7Yg==} + 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.1.0': + resolution: {integrity: sha512-axr/EZ4CKKPW2rzSkNpC9rG5H5I93sQdJFs60SMXMUMnlzc9FdxTTBldlbNntEroKu3ff6ghXkL9o/2SIW6erw==} + 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.1.0': + resolution: {integrity: sha512-lIW66Ly9yOa6SXCAAw07N99miqkEpRQoOYgyVEjMVX2Us+g1+UdnCOJ5DKkpkE1q68RY9C1qcZHnw54yTSjWWw==} + 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.1.0': + resolution: {integrity: sha512-/R84MZZwW6U3a13QNt2uHDyir26r/ETAa3xuj4XWsQ9Y9GyxwIYQmOerrEQWzLruXhuP9sSomRWRmzON3QIxVw==} + 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.1.0': + resolution: {integrity: sha512-Hc8cKx5ZHiz2cW/OlfpCSVUNS7kH9oxcOmVfdWZWeF8L/1QM7pYAcq29WyC9X2jrX5TvHqB0jqouOBNElrSbTA==} + 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.1.0': + resolution: {integrity: sha512-6975HRg5RBFZrnaYqJxSZ861CSTorogq4pBZ6NfrjPFzQdo8nlmA0Fpbc9aYk4uV0l3o70ZPyVtr5w6/fazTwg==} + 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.1.0': + resolution: {integrity: sha512-XOqc8y4WK2Kg/so7LfNWEy6eQ7DE8nGKxvh9ouVeayo3QcggGgFkWpBiw3fx2l6otFDTXFKkT3v7J/yT8rm/Ng==} + 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.1.0': + resolution: {integrity: sha512-YXaws30Q0gMTu/4h0y/T+MYtQr8MS/XQo4PTQts4TAmCVaxU2+yZra+kNO9Yj7DEcir9vDnb18Hb95/hnErDfg==} + 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.1.0': + resolution: {integrity: sha512-VLVXn+QVhb58bCxBvEBSvxSapEOKMJ71rMG5RSRza8JyV6fgZSxMme34M7SJYB5s2OMcyRg91FiIaae+nKIgOg==} + 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.1.0': + resolution: {integrity: sha512-bUYCr2pm/F3M7mTmnVZ/R7XjAmDd0fxO5bGVlryJ4ZGOAQI65y003nEwlSaNGDx3g/ZkjzpshchJHhaeMlYM/Q==} + 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.1.0': + resolution: {integrity: sha512-AbZYIxPbosnM3rFUwntvMLOZex8syNaU+StvlriB74XZyLt3mjC/V5aA1/Gv6XmS/aaJ74OqQ5QjS2ui1VCgBQ==} + 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.1.0': + resolution: {integrity: sha512-fH29RT9c0VwgS1rca9doPAalXJ0YAzvnpuy3VoOEqMCr9NvgLMpP6k9EuJAfBBNpek9u0uErTz72qIPbObHNwA==} + 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.12: + resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + 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 + + '@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.13.0(@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.1.0(typescript@5.9.3))': + dependencies: + '@solana/kit': 8.1.0(typescript@5.9.3) + + '@solana-program/token@0.15.0(@solana/kit@7.1.1(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.13.0(@solana/kit@7.1.1(typescript@5.9.3)) + '@solana/kit': 7.1.1(typescript@5.9.3) + + '@solana-program/token@0.16.0(@solana/kit@8.1.0(typescript@5.9.3))': + dependencies: + '@solana-program/system': 0.14.0(@solana/kit@8.1.0(typescript@5.9.3)) + '@solana/kit': 8.1.0(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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/assertions': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/fixed-points': 8.1.0(typescript@5.9.3) + '@solana/options': 8.1.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.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/assertions': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.1.0(typescript@5.9.3) + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/instruction-plans': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/offchain-messages': 8.1.0(typescript@5.9.3) + '@solana/plugin-core': 8.1.0(typescript@5.9.3) + '@solana/plugin-interfaces': 8.1.0(typescript@5.9.3) + '@solana/program-client-core': 8.1.0(typescript@5.9.3) + '@solana/programs': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.0(typescript@5.9.3) + '@solana/rpc': 8.1.0(typescript@5.9.3) + '@solana/rpc-api': 8.1.0(typescript@5.9.3) + '@solana/rpc-parsed-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/signers': 8.1.0(typescript@5.9.3) + '@solana/subscribable': 8.1.0(typescript@5.9.3) + '@solana/sysvars': 8.1.0(typescript@5.9.3) + '@solana/transaction-confirmation': 8.1.0(typescript@5.9.3) + '@solana/transaction-introspection': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.1.0(typescript@5.9.3) + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/instruction-plans': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/signers': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.1.0(typescript@5.9.3) + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/instruction-plans': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/plugin-interfaces': 8.1.0(typescript@5.9.3) + '@solana/rpc-api': 8.1.0(typescript@5.9.3) + '@solana/signers': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/rpc-parsed-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/subscribable': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.1.0(typescript@5.9.3) + '@solana/subscribable': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/subscribable': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/subscribable': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/fixed-points': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/rpc-api': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec': 8.1.0(typescript@5.9.3) + '@solana/rpc-spec-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-transformers': 8.1.0(typescript@5.9.3) + '@solana/rpc-transport-http': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.0(typescript@5.9.3) + '@solana/offchain-messages': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/accounts': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/promises': 8.1.0(typescript@5.9.3) + '@solana/rpc': 8.1.0(typescript@5.9.3) + '@solana/rpc-subscriptions': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.0(typescript@5.9.3) + '@solana/transactions': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.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.1.0(typescript@5.9.3)': + dependencies: + '@solana/addresses': 8.1.0(typescript@5.9.3) + '@solana/codecs-core': 8.1.0(typescript@5.9.3) + '@solana/codecs-data-structures': 8.1.0(typescript@5.9.3) + '@solana/codecs-numbers': 8.1.0(typescript@5.9.3) + '@solana/codecs-strings': 8.1.0(typescript@5.9.3) + '@solana/errors': 8.1.0(typescript@5.9.3) + '@solana/functional': 8.1.0(typescript@5.9.3) + '@solana/instructions': 8.1.0(typescript@5.9.3) + '@solana/keys': 8.1.0(typescript@5.9.3) + '@solana/nominal-types': 8.1.0(typescript@5.9.3) + '@solana/rpc-types': 8.1.0(typescript@5.9.3) + '@solana/transaction-messages': 8.1.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.1.0(typescript@5.9.3)) + '@solana-program/token': 0.16.0(@solana/kit@8.1.0(typescript@5.9.3)) + '@solana/kit': 8.1.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.12: + 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-fundraiser/pinocchio/program/Cargo.toml b/tokens/token-fundraiser/pinocchio/program/Cargo.toml new file mode 100644 index 000000000..c7d3e3134 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fundraiser-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-fundraiser/pinocchio/program/src/error.rs b/tokens/token-fundraiser/pinocchio/program/src/error.rs new file mode 100644 index 000000000..542c89212 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/error.rs @@ -0,0 +1,29 @@ +use pinocchio::error::ProgramError; + +/// Domain errors returned by the fundraiser program, surfaced to clients as +/// `ProgramError::Custom(code)`. +#[repr(u32)] +pub enum FundraiserError { + /// The target amount is below the minimum allowed. + InvalidAmount, + /// The contribution is below the minimum allowed. + ContributionTooSmall, + /// The contribution exceeds the per-contributor maximum. + ContributionTooBig, + /// The fundraiser's duration has already elapsed. + FundraiserEnded, + /// This contributor has reached the per-contributor maximum. + MaximumContributionsReached, + /// The target amount has not been reached yet. + TargetNotMet, + /// The target amount has already been reached. + TargetMet, + /// The fundraiser's duration has not elapsed yet. + FundraiserNotEnded, +} + +impl From for ProgramError { + fn from(error: FundraiserError) -> Self { + ProgramError::Custom(error as u32) + } +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs new file mode 100644 index 000000000..e9287bd75 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs @@ -0,0 +1,110 @@ +use pinocchio::{ + cpi::{Seed, Signer}, + error::ProgramError, + AccountView, Address, ProgramResult, +}; +use pinocchio_associated_token_account::instructions::CreateIdempotent; +use pinocchio_log::log; +use pinocchio_token::{ + instructions::{CloseAccount, Transfer}, + state::Account as TokenAccount, +}; + +use crate::{error::FundraiserError, state::Fundraiser}; + +/// Releases the raised funds to the maker once the target is met, then closes +/// the vault and fundraiser accounts. Only the maker may call this. +/// +/// Accounts: +/// 0. `[signer, writable]` maker (receives the funds and reclaimed rent) +/// 1. `[]` mint to raise +/// 2. `[writable]` fundraiser account (PDA `[b"fundraiser", maker]`, closed here) +/// 3. `[writable]` vault (fundraiser PDA's token account, drained and closed) +/// 4. `[writable]` maker's token account (destination, created if needed) +/// 5. `[]` token program +/// 6. `[]` associated token program +/// 7. `[]` system program +/// +/// Instruction data: none. +pub fn check_contributions(program_id: &Address, accounts: &mut [AccountView], _data: &[u8]) -> ProgramResult { + let [maker, mint_to_raise, fundraiser, vault, maker_ata, token_program, _associated_token_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !maker.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let state = Fundraiser::deserialize(&fundraiser.try_borrow()?)?; + + // Only the maker who created this fundraiser may release the funds. + if &state.maker != maker.address().as_array() || &state.mint_to_raise != mint_to_raise.address().as_array() { + return Err(ProgramError::InvalidAccountData); + } + let bump_bytes = [state.bump]; + let fundraiser_pda = + Address::create_program_address(&[Fundraiser::SEED_PREFIX, maker.address().as_ref(), &bump_bytes], program_id) + .map_err(|_| ProgramError::InvalidSeeds)?; + if fundraiser.address() != &fundraiser_pda { + return Err(ProgramError::InvalidSeeds); + } + let (expected_vault, _) = Address::find_program_address( + &[fundraiser.address().as_ref(), pinocchio_token::ID.as_ref(), mint_to_raise.address().as_ref()], + &pinocchio_associated_token_account::ID, + ); + if vault.address() != &expected_vault { + return Err(ProgramError::InvalidAccountData); + } + + // The target must have been reached. + let vault_amount = TokenAccount::from_account_view(vault)?.amount(); + if vault_amount < state.amount_to_raise { + return Err(FundraiserError::TargetNotMet.into()); + } + + // Ensure the maker has a token account to receive into. + log!("Ensuring maker token account exists"); + CreateIdempotent { + funding_account: maker, + account: maker_ata, + wallet: maker, + mint: mint_to_raise, + system_program, + token_program, + } + .invoke()?; + + // Release the funds and tear down the vault, both signed by the fundraiser PDA. + let seeds = [Seed::from(Fundraiser::SEED_PREFIX), Seed::from(maker.address().as_ref()), Seed::from(&bump_bytes)]; + let signers = [Signer::from(&seeds)]; + + log!("Releasing funds to maker"); + Transfer { + from: vault, + to: maker_ata, + authority: fundraiser, + multisig_signers: &[] as &[&AccountView], + amount: vault_amount, + } + .invoke_signed(&signers)?; + + log!("Closing vault"); + CloseAccount { + account: vault, + destination: maker, + authority: fundraiser, + multisig_signers: &[] as &[&AccountView], + } + .invoke_signed(&signers)?; + + // Close the fundraiser account, returning its rent to the maker. + log!("Closing fundraiser account"); + let new_maker_lamports = maker.lamports() + fundraiser.lamports(); + maker.set_lamports(new_maker_lamports); + fundraiser.close()?; + + log!("Funds released"); + Ok(()) +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs new file mode 100644 index 000000000..656367eac --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs @@ -0,0 +1,134 @@ +use pinocchio::{ + cpi::{Seed, Signer}, + error::ProgramError, + sysvars::{rent::Rent, Sysvar}, + AccountView, Address, ProgramResult, +}; +use pinocchio_log::log; +use pinocchio_system::instructions::CreateAccount; +use pinocchio_token::instructions::Transfer; + +use crate::{ + error::FundraiserError, + instructions::{days_elapsed, max_contribution, read_u64}, + state::{Contributor, Fundraiser}, +}; + +/// Contributes `amount` of the raised token into the fundraiser's vault, up to a +/// per-contributor cap, while the fundraiser is still running. +/// +/// Accounts: +/// 0. `[signer, writable]` contributor (funds the contributor account; source authority) +/// 1. `[]` mint to raise +/// 2. `[writable]` fundraiser account (PDA `[b"fundraiser", maker]`) +/// 3. `[writable]` contributor account (PDA `[b"contributor", fundraiser, contributor]`, created if needed) +/// 4. `[writable]` contributor's token account (source) +/// 5. `[writable]` vault (fundraiser PDA's token account) +/// 6. `[]` token program +/// 7. `[]` system program +/// +/// Instruction data: `[amount: u64 (LE), contributor_bump: u8]` +pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [contributor, mint_to_raise, fundraiser, contributor_account, contributor_ata, vault, _token_program, _system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !contributor.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let amount = read_u64(data, 0)?; + let contributor_bump = *data.get(8).ok_or(ProgramError::InvalidInstructionData)?; + + let mut state = Fundraiser::deserialize(&fundraiser.try_borrow()?)?; + + // Confirm the fundraiser PDA and that the vault is its associated token account. + let fundraiser_bump = [state.bump]; + let fundraiser_pda = + Address::create_program_address(&[Fundraiser::SEED_PREFIX, state.maker.as_ref(), &fundraiser_bump], program_id) + .map_err(|_| ProgramError::InvalidSeeds)?; + if fundraiser.address() != &fundraiser_pda || &state.mint_to_raise != mint_to_raise.address().as_array() { + return Err(ProgramError::InvalidAccountData); + } + let (expected_vault, _) = Address::find_program_address( + &[fundraiser.address().as_ref(), pinocchio_token::ID.as_ref(), mint_to_raise.address().as_ref()], + &pinocchio_associated_token_account::ID, + ); + if vault.address() != &expected_vault { + return Err(ProgramError::InvalidAccountData); + } + + // Contribution bounds: at least one base unit, at most the per-contributor cap. + if amount == 0 { + return Err(FundraiserError::ContributionTooSmall.into()); + } + let cap = max_contribution(state.amount_to_raise); + if amount > cap { + return Err(FundraiserError::ContributionTooBig.into()); + } + + // The fundraiser must still be running. + if days_elapsed(state.time_started)? >= state.duration as i64 { + return Err(FundraiserError::FundraiserEnded.into()); + } + + // Create the per-contributor record on first contribution; otherwise load it. + let already_contributed = if contributor_account.owner() == program_id { + Contributor::deserialize(&contributor_account.try_borrow()?)?.amount + } else { + let bump_bytes = [contributor_bump]; + let seeds = [ + Seed::from(Contributor::SEED_PREFIX), + Seed::from(fundraiser.address().as_ref()), + Seed::from(contributor.address().as_ref()), + Seed::from(&bump_bytes), + ]; + let contributor_pda = Address::create_program_address( + &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref(), &bump_bytes], + program_id, + ) + .map_err(|_| ProgramError::InvalidSeeds)?; + if contributor_account.address() != &contributor_pda { + return Err(ProgramError::InvalidSeeds); + } + + log!("Creating contributor account"); + let lamports = Rent::get()?.try_minimum_balance(Contributor::LEN)?; + CreateAccount { + from: contributor, + to: contributor_account, + lamports, + space: Contributor::LEN as u64, + owner: program_id, + } + .invoke_signed(&[Signer::from(&seeds)])?; + 0 + }; + + // Enforce the per-contributor cap across this contributor's total. + let new_total = already_contributed.checked_add(amount).ok_or(FundraiserError::ContributionTooBig)?; + if new_total > cap { + return Err(FundraiserError::MaximumContributionsReached.into()); + } + + // Move the tokens into the vault, then record the contribution. + log!("Transferring contribution to vault"); + Transfer { + from: contributor_ata, + to: vault, + authority: contributor, + multisig_signers: &[] as &[&AccountView], + amount, + } + .invoke()?; + + state.current_amount = state.current_amount.checked_add(amount).ok_or(ProgramError::ArithmeticOverflow)?; + state.serialize(&mut fundraiser.try_borrow_mut()?)?; + + Contributor { amount: new_total }.serialize(&mut contributor_account.try_borrow_mut()?)?; + + log!("Contribution recorded"); + Ok(()) +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/initialize.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/initialize.rs new file mode 100644 index 000000000..99e0658c1 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/initialize.rs @@ -0,0 +1,94 @@ +use pinocchio::{ + cpi::{Seed, Signer}, + error::ProgramError, + sysvars::{clock::Clock, rent::Rent, Sysvar}, + AccountView, Address, ProgramResult, +}; +use pinocchio_associated_token_account::instructions::CreateIdempotent; +use pinocchio_log::log; +use pinocchio_system::instructions::CreateAccount; + +use crate::{ + error::FundraiserError, + instructions::{mint_decimals, read_u16, read_u64, MIN_AMOUNT_TO_RAISE}, + state::Fundraiser, +}; + +/// Starts a fundraiser: creates the fundraiser PDA state account and its vault +/// token account, which will hold contributed tokens until the target is met. +/// +/// Accounts: +/// 0. `[writable]` fundraiser account (PDA `[b"fundraiser", maker]`, created here) +/// 1. `[]` mint to raise +/// 2. `[writable]` vault (fundraiser PDA's associated token account, created here) +/// 3. `[signer, writable]` maker (funds the accounts; receives the funds) +/// 4. `[]` token program +/// 5. `[]` associated token program +/// 6. `[]` system program +/// +/// Instruction data: `[amount: u64 (LE), duration: u16 (LE), bump: u8]` +pub fn initialize(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { + let [fundraiser, mint_to_raise, vault, maker, token_program, _associated_token_program, system_program] = accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !maker.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let amount = read_u64(data, 0)?; + let duration = read_u16(data, 8)?; + let bump = *data.get(10).ok_or(ProgramError::InvalidInstructionData)?; + + // The target must clear the minimum, scaled by the mint's decimals. + let decimals = mint_decimals(mint_to_raise)?; + let minimum = MIN_AMOUNT_TO_RAISE.checked_pow(decimals as u32).ok_or(FundraiserError::InvalidAmount)?; + if amount < minimum { + return Err(FundraiserError::InvalidAmount.into()); + } + + // Confirm the supplied account is the canonical fundraiser PDA. + let bump_bytes = [bump]; + let seeds = [Seed::from(Fundraiser::SEED_PREFIX), Seed::from(maker.address().as_ref()), Seed::from(&bump_bytes)]; + let fundraiser_pda = + Address::create_program_address(&[Fundraiser::SEED_PREFIX, maker.address().as_ref(), &bump_bytes], program_id) + .map_err(|_| ProgramError::InvalidSeeds)?; + if fundraiser.address() != &fundraiser_pda { + return Err(ProgramError::InvalidSeeds); + } + + // Create the fundraiser state account, signed by the PDA itself. + log!("Creating fundraiser account"); + let lamports = Rent::get()?.try_minimum_balance(Fundraiser::LEN)?; + let signers = [Signer::from(&seeds)]; + CreateAccount { from: maker, to: fundraiser, lamports, space: Fundraiser::LEN as u64, owner: program_id } + .invoke_signed(&signers)?; + + // Create the vault: the fundraiser PDA's associated token account. + log!("Creating vault"); + CreateIdempotent { + funding_account: maker, + account: vault, + wallet: fundraiser, + mint: mint_to_raise, + system_program, + token_program, + } + .invoke()?; + + // Persist the fundraiser state. + let state = Fundraiser { + maker: *maker.address().as_array(), + mint_to_raise: *mint_to_raise.address().as_array(), + amount_to_raise: amount, + current_amount: 0, + time_started: Clock::get()?.unix_timestamp, + duration, + bump, + }; + state.serialize(&mut fundraiser.try_borrow_mut()?)?; + + log!("Fundraiser created"); + Ok(()) +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/mod.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/mod.rs new file mode 100644 index 000000000..07cae1c04 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/mod.rs @@ -0,0 +1,57 @@ +use pinocchio::error::ProgramError; + +mod check_contributions; +mod contribute; +mod initialize; +mod refund; + +pub use check_contributions::*; +pub use contribute::*; +pub use initialize::*; +pub use refund::*; + +/// Minimum target (before scaling by the mint's decimals) a fundraiser may set. +pub const MIN_AMOUNT_TO_RAISE: u64 = 3; +/// Seconds in a day; the fundraiser duration is expressed in days. +pub const SECONDS_TO_DAYS: i64 = 86_400; +/// A single contributor may contribute at most this percentage of the target. +pub const MAX_CONTRIBUTION_PERCENTAGE: u64 = 10; +/// Denominator for the percentage above. +pub const PERCENTAGE_SCALER: u64 = 100; +/// The mint's `decimals` field sits at byte offset 44 of the base mint layout. +pub const MINT_DECIMALS_OFFSET: usize = 44; + +/// Reads a little-endian `u64` starting at `offset` within `data`. +pub(crate) fn read_u64(data: &[u8], offset: usize) -> Result { + data.get(offset..offset + 8) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(ProgramError::InvalidInstructionData) +} + +/// Reads a little-endian `u16` starting at `offset` within `data`. +pub(crate) fn read_u16(data: &[u8], offset: usize) -> Result { + data.get(offset..offset + 2) + .and_then(|slice| slice.try_into().ok()) + .map(u16::from_le_bytes) + .ok_or(ProgramError::InvalidInstructionData) +} + +/// Reads the `decimals` field from a base SPL mint account. +pub(crate) fn mint_decimals(mint: &pinocchio::AccountView) -> Result { + let data = mint.try_borrow()?; + data.get(MINT_DECIMALS_OFFSET).copied().ok_or(ProgramError::InvalidAccountData) +} + +/// The maximum a single contributor may contribute: `MAX_CONTRIBUTION_PERCENTAGE` +/// percent of the target. +pub(crate) fn max_contribution(amount_to_raise: u64) -> u64 { + amount_to_raise.saturating_mul(MAX_CONTRIBUTION_PERCENTAGE) / PERCENTAGE_SCALER +} + +/// Number of whole days elapsed since `time_started`, using the Clock sysvar. +pub(crate) fn days_elapsed(time_started: i64) -> Result { + use pinocchio::sysvars::{clock::Clock, Sysvar}; + let now = Clock::get()?.unix_timestamp; + Ok((now - time_started) / SECONDS_TO_DAYS) +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs new file mode 100644 index 000000000..cc12b2b36 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs @@ -0,0 +1,103 @@ +use pinocchio::{ + cpi::{Seed, Signer}, + error::ProgramError, + AccountView, Address, ProgramResult, +}; +use pinocchio_log::log; +use pinocchio_token::{instructions::Transfer, state::Account as TokenAccount}; + +use crate::{ + error::FundraiserError, + instructions::days_elapsed, + state::{Contributor, Fundraiser}, +}; + +/// Refunds a contributor once the fundraiser has ended without meeting its +/// target, returning their contribution and closing their contributor account. +/// +/// Accounts: +/// 0. `[signer, writable]` contributor (receives the refund and reclaimed rent) +/// 1. `[]` maker (part of the fundraiser PDA seeds) +/// 2. `[]` mint to raise +/// 3. `[writable]` fundraiser account (PDA `[b"fundraiser", maker]`) +/// 4. `[writable]` contributor account (PDA, closed here) +/// 5. `[writable]` contributor's token account (destination) +/// 6. `[writable]` vault (fundraiser PDA's token account) +/// 7. `[]` token program +/// +/// Instruction data: none. +pub fn refund(program_id: &Address, accounts: &mut [AccountView], _data: &[u8]) -> ProgramResult { + let [contributor, maker, mint_to_raise, fundraiser, contributor_account, contributor_ata, vault, _token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if !contributor.is_signer() { + return Err(ProgramError::MissingRequiredSignature); + } + + let mut state = Fundraiser::deserialize(&fundraiser.try_borrow()?)?; + + // The fundraiser PDA is derived from the maker; confirm both, and the vault. + if &state.maker != maker.address().as_array() || &state.mint_to_raise != mint_to_raise.address().as_array() { + return Err(ProgramError::InvalidAccountData); + } + let bump_bytes = [state.bump]; + let fundraiser_pda = + Address::create_program_address(&[Fundraiser::SEED_PREFIX, maker.address().as_ref(), &bump_bytes], program_id) + .map_err(|_| ProgramError::InvalidSeeds)?; + if fundraiser.address() != &fundraiser_pda { + return Err(ProgramError::InvalidSeeds); + } + let (expected_vault, _) = Address::find_program_address( + &[fundraiser.address().as_ref(), pinocchio_token::ID.as_ref(), mint_to_raise.address().as_ref()], + &pinocchio_associated_token_account::ID, + ); + if vault.address() != &expected_vault { + return Err(ProgramError::InvalidAccountData); + } + + // Refunds are only allowed once the fundraiser has ended without meeting its target. + if days_elapsed(state.time_started)? < state.duration as i64 { + return Err(FundraiserError::FundraiserNotEnded.into()); + } + let vault_amount = TokenAccount::from_account_view(vault)?.amount(); + if vault_amount >= state.amount_to_raise { + return Err(FundraiserError::TargetMet.into()); + } + + // Confirm the contributor account is the canonical PDA before trusting it. + let contributor_state = Contributor::deserialize(&contributor_account.try_borrow()?)?; + let (contributor_pda, _) = Address::find_program_address( + &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref()], + program_id, + ); + if contributor_account.address() != &contributor_pda { + return Err(ProgramError::InvalidSeeds); + } + + // Return the contribution, signed by the fundraiser PDA. + let seeds = [Seed::from(Fundraiser::SEED_PREFIX), Seed::from(maker.address().as_ref()), Seed::from(&bump_bytes)]; + log!("Refunding contributor"); + Transfer { + from: vault, + to: contributor_ata, + authority: fundraiser, + multisig_signers: &[] as &[&AccountView], + amount: contributor_state.amount, + } + .invoke_signed(&[Signer::from(&seeds)])?; + + // Reduce the recorded total and close the contributor account. + state.current_amount = state.current_amount.saturating_sub(contributor_state.amount); + state.serialize(&mut fundraiser.try_borrow_mut()?)?; + + log!("Closing contributor account"); + let new_contributor_lamports = contributor.lamports() + contributor_account.lamports(); + contributor.set_lamports(new_contributor_lamports); + contributor_account.close()?; + + log!("Refund complete"); + Ok(()) +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/lib.rs b/tokens/token-fundraiser/pinocchio/program/src/lib.rs new file mode 100644 index 000000000..1e5b927ea --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/lib.rs @@ -0,0 +1,11 @@ +#![no_std] + +pub mod error; +pub mod instructions; +pub mod processor; +pub mod state; + +use pinocchio::{entrypoint, nostd_panic_handler}; + +entrypoint!(processor::process_instruction); +nostd_panic_handler!(); diff --git a/tokens/token-fundraiser/pinocchio/program/src/processor.rs b/tokens/token-fundraiser/pinocchio/program/src/processor.rs new file mode 100644 index 000000000..dfa21a63f --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/processor.rs @@ -0,0 +1,39 @@ +use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult}; +use pinocchio_log::log; + +use crate::instructions::{check_contributions, contribute, initialize, refund}; + +/// Dispatches an instruction based on its leading discriminator byte. +/// +/// Instruction data layout: `[discriminator: u8, ..args]` +/// - `0` -> Initialize (args: `[amount: u64 (LE), duration: u16 (LE), bump: u8]`) +/// - `1` -> Contribute (args: `[amount: u64 (LE), contributor_bump: u8]`) +/// - `2` -> CheckContributions (no args) +/// - `3` -> Refund (no args) +pub fn process_instruction( + program_id: &Address, + accounts: &mut [AccountView], + instruction_data: &[u8], +) -> ProgramResult { + let (discriminator, args) = instruction_data.split_first().ok_or(ProgramError::InvalidInstructionData)?; + + match *discriminator { + 0 => { + log!("Instruction: Initialize"); + initialize(program_id, accounts, args) + } + 1 => { + log!("Instruction: Contribute"); + contribute(program_id, accounts, args) + } + 2 => { + log!("Instruction: CheckContributions"); + check_contributions(program_id, accounts, args) + } + 3 => { + log!("Instruction: Refund"); + refund(program_id, accounts, args) + } + _ => Err(ProgramError::InvalidInstructionData), + } +} diff --git a/tokens/token-fundraiser/pinocchio/program/src/state.rs b/tokens/token-fundraiser/pinocchio/program/src/state.rs new file mode 100644 index 000000000..0872d5e45 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/program/src/state.rs @@ -0,0 +1,88 @@ +use pinocchio::error::ProgramError; + +/// Persistent state of a fundraiser, stored in the fundraiser PDA. +/// +/// The fundraiser PDA is derived from `[b"fundraiser", maker]` and is the +/// authority of the vault token account that holds contributed tokens. +/// +/// Serialized byte layout (little-endian), matching the field order below: +/// `[maker: 32][mint_to_raise: 32][amount_to_raise: u64][current_amount: u64] +/// [time_started: i64][duration: u16][bump: u8]` +pub struct Fundraiser { + /// The wallet that created the fundraiser and receives the funds. + pub maker: [u8; 32], + /// Mint of the token being raised. + pub mint_to_raise: [u8; 32], + /// Target amount to raise before the funds can be released. + pub amount_to_raise: u64, + /// Amount contributed so far. + pub current_amount: u64, + /// Unix timestamp when the fundraiser started. + pub time_started: i64, + /// Fundraiser duration, in days. + pub duration: u16, + /// Canonical bump for the fundraiser PDA. + pub bump: u8, +} + +impl Fundraiser { + /// Seed prefix for the fundraiser PDA: `[SEED_PREFIX, maker]`. + pub const SEED_PREFIX: &'static [u8] = b"fundraiser"; + + /// Serialized size in bytes. + pub const LEN: usize = 32 + 32 + 8 + 8 + 8 + 2 + 1; + + pub fn serialize(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + let dst = dst.get_mut(..Self::LEN).ok_or(ProgramError::AccountDataTooSmall)?; + dst[0..32].copy_from_slice(&self.maker); + dst[32..64].copy_from_slice(&self.mint_to_raise); + dst[64..72].copy_from_slice(&self.amount_to_raise.to_le_bytes()); + dst[72..80].copy_from_slice(&self.current_amount.to_le_bytes()); + dst[80..88].copy_from_slice(&self.time_started.to_le_bytes()); + dst[88..90].copy_from_slice(&self.duration.to_le_bytes()); + dst[90] = self.bump; + Ok(()) + } + + pub fn deserialize(src: &[u8]) -> Result { + let src: &[u8; Self::LEN] = + src.get(..Self::LEN).and_then(|s| s.try_into().ok()).ok_or(ProgramError::InvalidAccountData)?; + Ok(Self { + maker: src[0..32].try_into().unwrap(), + mint_to_raise: src[32..64].try_into().unwrap(), + amount_to_raise: u64::from_le_bytes(src[64..72].try_into().unwrap()), + current_amount: u64::from_le_bytes(src[72..80].try_into().unwrap()), + time_started: i64::from_le_bytes(src[80..88].try_into().unwrap()), + duration: u16::from_le_bytes(src[88..90].try_into().unwrap()), + bump: src[90], + }) + } +} + +/// Per-contributor record of how much a wallet has contributed, stored in the +/// contributor PDA derived from `[b"contributor", fundraiser, contributor]`. +/// +/// Serialized byte layout: `[amount: u64]` (little-endian). +pub struct Contributor { + pub amount: u64, +} + +impl Contributor { + /// Seed prefix for the contributor PDA: `[SEED_PREFIX, fundraiser, contributor]`. + pub const SEED_PREFIX: &'static [u8] = b"contributor"; + + /// Serialized size in bytes. + pub const LEN: usize = 8; + + pub fn serialize(&self, dst: &mut [u8]) -> Result<(), ProgramError> { + let dst = dst.get_mut(..Self::LEN).ok_or(ProgramError::AccountDataTooSmall)?; + dst[0..8].copy_from_slice(&self.amount.to_le_bytes()); + Ok(()) + } + + pub fn deserialize(src: &[u8]) -> Result { + let src: &[u8; Self::LEN] = + src.get(..Self::LEN).and_then(|s| s.try_into().ok()).ok_or(ProgramError::InvalidAccountData)?; + Ok(Self { amount: u64::from_le_bytes(*src) }) + } +} diff --git a/tokens/token-fundraiser/pinocchio/tests/test.ts b/tokens/token-fundraiser/pinocchio/tests/test.ts new file mode 100644 index 000000000..d36b6c9c0 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/tests/test.ts @@ -0,0 +1,349 @@ +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 { getCreateAccountInstruction, SYSTEM_PROGRAM_ADDRESS } from '@solana-program/system'; +import { + ASSOCIATED_TOKEN_PROGRAM_ADDRESS, + findAssociatedTokenPda, + getCreateAssociatedTokenInstruction, + getInitializeMint2Instruction, + getMintToInstruction, + getTokenDecoder, + TOKEN_PROGRAM_ADDRESS, +} from '@solana-program/token'; +import { assert } from 'chai'; +import { FailedTransactionMetadata, LiteSVM } from 'litesvm'; + +const MINT_SIZE = 82n; +const SECONDS_PER_DAY = 86_400n; + +const PROGRAM_SO = path.join(process.cwd(), 'tests', 'fixtures', 'fundraiser_pinocchio_program.so'); +const addressEncoder = getAddressEncoder(); + +// Little-endian encoders for the raw instruction data the program expects. +function u64(n: bigint): Uint8Array { + const b = new Uint8Array(8); + new DataView(b.buffer).setBigUint64(0, n, true); + return b; +} +function u16(n: number): Uint8Array { + const b = new Uint8Array(2); + new DataView(b.buffer).setUint16(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; +} + +describe('Token Fundraiser (Pinocchio)', () => { + let svm: LiteSVM; + let programId: Address; + + before(async () => { + svm = new LiteSVM(); + programId = (await generateKeyPairSigner()).address; + svm.addProgramFromFile(programId, PROGRAM_SO); + }); + + function send(signedTx: Parameters[0], label: string) { + const result = svm.sendTransaction(signedTx); + if (result instanceof FailedTransactionMetadata) { + throw new Error(`${label} failed: ${result.err()}`); + } + } + + async function tx(payer: KeyPairSigner, instructions: Parameters[0][]) { + return signTransactionMessageWithSigners( + pipe( + createTransactionMessage({ version: 0 }), + m => setTransactionMessageFeePayerSigner(payer, m), + m => svm.setTransactionMessageLifetimeUsingLatestBlockhash(m), + m => appendTransactionMessageInstructions(instructions, m), + ), + ); + } + + // Creates a plain SPL mint with the given decimals and authority. + async function createMint(payer: KeyPairSigner, decimals: number, mintAuthority: Address): Promise { + const mint = await generateKeyPairSigner(); + send( + await tx(payer, [ + getCreateAccountInstruction({ + payer, + newAccount: mint, + lamports: svm.minimumBalanceForRentExemption(MINT_SIZE), + space: MINT_SIZE, + programAddress: TOKEN_PROGRAM_ADDRESS, + }), + getInitializeMint2Instruction({ mint: mint.address, decimals, mintAuthority, freezeAuthority: null }), + ]), + 'create mint', + ); + return mint; + } + + // Creates `owner`'s associated token account and mints `amount` into it. + async function fundAta( + payer: KeyPairSigner, + mintAuthority: KeyPairSigner, + owner: Address, + mint: Address, + amount: bigint, + ): Promise
{ + const [ata] = await findAssociatedTokenPda({ owner, mint, tokenProgram: TOKEN_PROGRAM_ADDRESS }); + send( + await tx(payer, [ + getCreateAssociatedTokenInstruction({ payer, ata, owner, mint }), + getMintToInstruction({ mint, token: ata, mintAuthority, amount }), + ]), + 'fund ata', + ); + return ata; + } + + function fundraiserPda(maker: Address) { + return getProgramDerivedAddress({ + programAddress: programId, + seeds: ['fundraiser', addressEncoder.encode(maker)], + }); + } + function contributorPda(fundraiser: Address, contributor: Address) { + return getProgramDerivedAddress({ + programAddress: programId, + seeds: ['contributor', addressEncoder.encode(fundraiser), addressEncoder.encode(contributor)], + }); + } + function tokenAmount(account: Address): bigint { + const acc = svm.getAccount(account); + if (!acc?.exists) throw new Error('token account not found'); + return getTokenDecoder().decode(acc.data).amount; + } + + function initializeIx( + maker: KeyPairSigner, + mint: Address, + fundraiser: Address, + vault: Address, + bump: number, + amount: bigint, + duration: number, + ) { + return { + programAddress: programId, + accounts: [ + { address: fundraiser, role: AccountRole.WRITABLE }, + { address: mint, role: AccountRole.READONLY }, + { address: vault, role: AccountRole.WRITABLE }, + { address: maker.address, role: AccountRole.WRITABLE_SIGNER, signer: maker }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(0), u64(amount), u16(duration), Uint8Array.of(bump)), + }; + } + + function contributeIx( + contributor: KeyPairSigner, + mint: Address, + fundraiser: Address, + contributorAccount: Address, + contributorAta: Address, + vault: Address, + bump: number, + amount: bigint, + ) { + return { + programAddress: programId, + accounts: [ + { address: contributor.address, role: AccountRole.WRITABLE_SIGNER, signer: contributor }, + { address: mint, role: AccountRole.READONLY }, + { address: fundraiser, role: AccountRole.WRITABLE }, + { address: contributorAccount, role: AccountRole.WRITABLE }, + { address: contributorAta, role: AccountRole.WRITABLE }, + { address: vault, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: concatBytes(Uint8Array.of(1), u64(amount), Uint8Array.of(bump)), + }; + } + + function refundIx( + contributor: KeyPairSigner, + maker: Address, + mint: Address, + fundraiser: Address, + contributorAccount: Address, + contributorAta: Address, + vault: Address, + ) { + return { + programAddress: programId, + accounts: [ + { address: contributor.address, role: AccountRole.WRITABLE_SIGNER, signer: contributor }, + { address: maker, role: AccountRole.READONLY }, + { address: mint, role: AccountRole.READONLY }, + { address: fundraiser, role: AccountRole.WRITABLE }, + { address: contributorAccount, role: AccountRole.WRITABLE }, + { address: contributorAta, role: AccountRole.WRITABLE }, + { address: vault, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: Uint8Array.of(3), + }; + } + + function checkIx(maker: KeyPairSigner, mint: Address, fundraiser: Address, vault: Address, makerAta: Address) { + return { + programAddress: programId, + accounts: [ + { address: maker.address, role: AccountRole.WRITABLE_SIGNER, signer: maker }, + { address: mint, role: AccountRole.READONLY }, + { address: fundraiser, role: AccountRole.WRITABLE }, + { address: vault, role: AccountRole.WRITABLE }, + { address: makerAta, role: AccountRole.WRITABLE }, + { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, + ], + data: Uint8Array.of(2), + }; + } + + function warpDays(days: number) { + const clock = svm.getClock(); + clock.unixTimestamp += BigInt(days) * SECONDS_PER_DAY; + svm.setClock(clock); + } + + it('Refunds a contributor after the fundraiser ends without meeting its target', async () => { + const maker = await generateKeyPairSigner(); + svm.airdrop(maker.address, lamports(1_000_000_000n)); + const mint = await createMint(maker, 0, maker.address); + + const [fundraiser, bump] = await fundraiserPda(maker.address); + const [vault] = await findAssociatedTokenPda({ + owner: fundraiser, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + + // Goal of 100 over 1 day; a single contributor gives the 10% max (10). + send(await tx(maker, [initializeIx(maker, mint.address, fundraiser, vault, bump, 100n, 1)]), 'initialize'); + + const contributor = await generateKeyPairSigner(); + svm.airdrop(contributor.address, lamports(1_000_000_000n)); + const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); + const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + + send( + await tx(contributor, [ + contributeIx( + contributor, + mint.address, + fundraiser, + contributorAccount, + contributorAta, + vault, + cbump, + 10n, + ), + ]), + 'contribute', + ); + + assert.equal(tokenAmount(vault), 10n, 'vault holds the contribution'); + assert.equal(tokenAmount(contributorAta), 0n, 'contributor spent their tokens'); + + // The fundraiser ends without meeting the 100 target; the contributor refunds. + warpDays(2); + send( + await tx(contributor, [ + refundIx( + contributor, + maker.address, + mint.address, + fundraiser, + contributorAccount, + contributorAta, + vault, + ), + ]), + 'refund', + ); + + assert.equal(tokenAmount(contributorAta), 10n, 'contributor got their tokens back'); + assert.equal(tokenAmount(vault), 0n, 'vault is empty after the refund'); + assert.isNotOk(svm.getAccount(contributorAccount)?.exists, 'contributor account was closed'); + }); + + it('Releases the funds to the maker once the target is met', async () => { + const maker = await generateKeyPairSigner(); + svm.airdrop(maker.address, lamports(1_000_000_000n)); + const mint = await createMint(maker, 0, maker.address); + + const [fundraiser, bump] = await fundraiserPda(maker.address); + const [vault] = await findAssociatedTokenPda({ + owner: fundraiser, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + + // Goal of 100 over 30 days; the 10% cap means it takes ten contributors. + send(await tx(maker, [initializeIx(maker, mint.address, fundraiser, vault, bump, 100n, 30)]), 'initialize'); + + for (let i = 0; i < 10; i++) { + const contributor = await generateKeyPairSigner(); + svm.airdrop(contributor.address, lamports(1_000_000_000n)); + const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); + const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + send( + await tx(contributor, [ + contributeIx( + contributor, + mint.address, + fundraiser, + contributorAccount, + contributorAta, + vault, + cbump, + 10n, + ), + ]), + `contribute ${i}`, + ); + } + + assert.equal(tokenAmount(vault), 100n, 'vault reached the target'); + + const [makerAta] = await findAssociatedTokenPda({ + owner: maker.address, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + send(await tx(maker, [checkIx(maker, mint.address, fundraiser, vault, makerAta)]), 'check contributions'); + + assert.equal(tokenAmount(makerAta), 100n, 'maker received the raised funds'); + assert.isNotOk(svm.getAccount(fundraiser)?.exists, 'fundraiser account was closed'); + }); +}); diff --git a/tokens/token-fundraiser/pinocchio/tsconfig.json b/tokens/token-fundraiser/pinocchio/tsconfig.json new file mode 100644 index 000000000..c02443141 --- /dev/null +++ b/tokens/token-fundraiser/pinocchio/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai", "node"], + "typeRoots": ["./node_modules/@types"], + "lib": ["esnext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true + } +} From c76d7161514eed3ffd056d33f15e086e2122109c Mon Sep 17 00:00:00 2001 From: MarkFeder Date: Sun, 30 Aug 2026 18:43:36 +0200 Subject: [PATCH 2/4] token-fundraiser: bind contributor record to signer and gate on recorded totals contribute skipped the contributor PDA derivation whenever the supplied record was already program-owned, so a signer could credit their transfer to another participant's record: it bypassed the per-contributor cap and left the tokens claimable by that record's owner through refund. The derivation now runs before the branch, for existing and new records alike. check_contributions and refund gated on the vault's token balance. The vault is a standard ATA, so any holder could transfer straight into it and push that balance past the target, releasing the fundraiser and blocking legitimate refunds without any recorded contribution. Both now read current_amount; the release still drains the full vault balance. Adds LiteSVM coverage for both: a contribution into a substituted record is rejected, and a direct vault transfer neither releases the fundraiser nor prevents a refund. Both tests fail against the previous program. --- .../src/instructions/check_contributions.rs | 6 +- .../program/src/instructions/contribute.rs | 34 ++--- .../program/src/instructions/refund.rs | 7 +- .../token-fundraiser/pinocchio/tests/test.ts | 124 ++++++++++++++++++ 4 files changed, 150 insertions(+), 21 deletions(-) diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs index e9287bd75..d8a813d2c 100644 --- a/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/check_contributions.rs @@ -58,9 +58,11 @@ pub fn check_contributions(program_id: &Address, accounts: &mut [AccountView], _ return Err(ProgramError::InvalidAccountData); } - // The target must have been reached. + // The target must have been reached by recorded contributions. The vault + // balance itself is not the gate: anyone can transfer into a standard ATA, + // and such unrecorded deposits must not release the fundraiser. let vault_amount = TokenAccount::from_account_view(vault)?.amount(); - if vault_amount < state.amount_to_raise { + if state.current_amount < state.amount_to_raise { return Err(FundraiserError::TargetNotMet.into()); } diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs index 656367eac..46b87e73b 100644 --- a/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs @@ -74,26 +74,28 @@ pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8 return Err(FundraiserError::FundraiserEnded.into()); } + // Bind the contributor record to this signer before trusting it, whether it + // already exists or is about to be created. + let bump_bytes = [contributor_bump]; + let seeds = [ + Seed::from(Contributor::SEED_PREFIX), + Seed::from(fundraiser.address().as_ref()), + Seed::from(contributor.address().as_ref()), + Seed::from(&bump_bytes), + ]; + let contributor_pda = Address::create_program_address( + &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref(), &bump_bytes], + program_id, + ) + .map_err(|_| ProgramError::InvalidSeeds)?; + if contributor_account.address() != &contributor_pda { + return Err(ProgramError::InvalidSeeds); + } + // Create the per-contributor record on first contribution; otherwise load it. let already_contributed = if contributor_account.owner() == program_id { Contributor::deserialize(&contributor_account.try_borrow()?)?.amount } else { - let bump_bytes = [contributor_bump]; - let seeds = [ - Seed::from(Contributor::SEED_PREFIX), - Seed::from(fundraiser.address().as_ref()), - Seed::from(contributor.address().as_ref()), - Seed::from(&bump_bytes), - ]; - let contributor_pda = Address::create_program_address( - &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref(), &bump_bytes], - program_id, - ) - .map_err(|_| ProgramError::InvalidSeeds)?; - if contributor_account.address() != &contributor_pda { - return Err(ProgramError::InvalidSeeds); - } - log!("Creating contributor account"); let lamports = Rent::get()?.try_minimum_balance(Contributor::LEN)?; CreateAccount { diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs index cc12b2b36..6adfac8bf 100644 --- a/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/refund.rs @@ -4,7 +4,7 @@ use pinocchio::{ AccountView, Address, ProgramResult, }; use pinocchio_log::log; -use pinocchio_token::{instructions::Transfer, state::Account as TokenAccount}; +use pinocchio_token::instructions::Transfer; use crate::{ error::FundraiserError, @@ -62,8 +62,9 @@ pub fn refund(program_id: &Address, accounts: &mut [AccountView], _data: &[u8]) if days_elapsed(state.time_started)? < state.duration as i64 { return Err(FundraiserError::FundraiserNotEnded.into()); } - let vault_amount = TokenAccount::from_account_view(vault)?.amount(); - if vault_amount >= state.amount_to_raise { + // Eligibility reads the recorded total, not the vault balance, so an + // unrecorded direct transfer into the vault cannot block legitimate refunds. + if state.current_amount >= state.amount_to_raise { return Err(FundraiserError::TargetMet.into()); } diff --git a/tokens/token-fundraiser/pinocchio/tests/test.ts b/tokens/token-fundraiser/pinocchio/tests/test.ts index d36b6c9c0..14305de9f 100644 --- a/tokens/token-fundraiser/pinocchio/tests/test.ts +++ b/tokens/token-fundraiser/pinocchio/tests/test.ts @@ -22,6 +22,7 @@ import { getInitializeMint2Instruction, getMintToInstruction, getTokenDecoder, + getTransferInstruction, TOKEN_PROGRAM_ADDRESS, } from '@solana-program/token'; import { assert } from 'chai'; @@ -71,6 +72,12 @@ describe('Token Fundraiser (Pinocchio)', () => { } } + // Asserts a transaction is rejected on-chain rather than succeeding. + function sendExpectingFailure(signedTx: Parameters[0], label: string) { + const result = svm.sendTransaction(signedTx); + assert.instanceOf(result, FailedTransactionMetadata, `${label} should have been rejected`); + } + async function tx(payer: KeyPairSigner, instructions: Parameters[0][]) { return signTransactionMessageWithSigners( pipe( @@ -346,4 +353,121 @@ describe('Token Fundraiser (Pinocchio)', () => { assert.equal(tokenAmount(makerAta), 100n, 'maker received the raised funds'); assert.isNotOk(svm.getAccount(fundraiser)?.exists, 'fundraiser account was closed'); }); + + it("Rejects a contribution credited to another contributor's record", async () => { + const maker = await generateKeyPairSigner(); + svm.airdrop(maker.address, lamports(1_000_000_000n)); + const mint = await createMint(maker, 0, maker.address); + + const [fundraiser, bump] = await fundraiserPda(maker.address); + const [vault] = await findAssociatedTokenPda({ + owner: fundraiser, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + send(await tx(maker, [initializeIx(maker, mint.address, fundraiser, vault, bump, 100n, 30)]), 'initialize'); + + // Victim contributes first, which creates their program-owned record. + // Amounts stay well under the 10% cap so that the substituted-record + // rejection cannot be attributed to the per-contributor limit. + const victim = await generateKeyPairSigner(); + svm.airdrop(victim.address, lamports(1_000_000_000n)); + const victimAta = await fundAta(maker, maker, victim.address, mint.address, 1n); + const [victimAccount, victimBump] = await contributorPda(fundraiser, victim.address); + send( + await tx(victim, [ + contributeIx(victim, mint.address, fundraiser, victimAccount, victimAta, vault, victimBump, 1n), + ]), + 'victim contribute', + ); + + // The attacker signs their own transfer but points at the victim's record. + const attacker = await generateKeyPairSigner(); + svm.airdrop(attacker.address, lamports(1_000_000_000n)); + const attackerAta = await fundAta(maker, maker, attacker.address, mint.address, 1n); + sendExpectingFailure( + await tx(attacker, [ + contributeIx(attacker, mint.address, fundraiser, victimAccount, attackerAta, vault, victimBump, 1n), + ]), + 'contribution into a substituted record', + ); + + assert.equal(tokenAmount(attackerAta), 1n, 'attacker kept their tokens'); + assert.equal(tokenAmount(vault), 1n, 'vault only holds the recorded contribution'); + }); + + it('Ignores unrecorded direct transfers into the vault', async () => { + const maker = await generateKeyPairSigner(); + svm.airdrop(maker.address, lamports(1_000_000_000n)); + const mint = await createMint(maker, 0, maker.address); + + const [fundraiser, bump] = await fundraiserPda(maker.address); + const [vault] = await findAssociatedTokenPda({ + owner: fundraiser, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + + // Goal of 100 over 1 day; a single contributor gives the 10% max (10). + send(await tx(maker, [initializeIx(maker, mint.address, fundraiser, vault, bump, 100n, 1)]), 'initialize'); + + const contributor = await generateKeyPairSigner(); + svm.airdrop(contributor.address, lamports(1_000_000_000n)); + const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); + const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + send( + await tx(contributor, [ + contributeIx( + contributor, + mint.address, + fundraiser, + contributorAccount, + contributorAta, + vault, + cbump, + 10n, + ), + ]), + 'contribute', + ); + + // Anyone can transfer straight into the vault's standard ATA. That must + // neither release the fundraiser nor lock the contributor out of a refund. + const outsiderAta = await fundAta(maker, maker, maker.address, mint.address, 90n); + send( + await tx(maker, [ + getTransferInstruction({ source: outsiderAta, destination: vault, authority: maker, amount: 90n }), + ]), + 'direct vault transfer', + ); + assert.equal(tokenAmount(vault), 100n, 'vault balance now looks like the target was met'); + + const [makerAta] = await findAssociatedTokenPda({ + owner: maker.address, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + sendExpectingFailure( + await tx(maker, [checkIx(maker, mint.address, fundraiser, vault, makerAta)]), + 'release on an unrecorded deposit', + ); + + // The contributor is still refundable once the fundraiser ends. + warpDays(2); + send( + await tx(contributor, [ + refundIx( + contributor, + maker.address, + mint.address, + fundraiser, + contributorAccount, + contributorAta, + vault, + ), + ]), + 'refund', + ); + assert.equal(tokenAmount(contributorAta), 10n, 'contributor got their tokens back'); + }); }); From f90ef59acfb05f46508696ddcb2b3e25391dd619 Mon Sep 17 00:00:00 2001 From: MarkFeder Date: Sun, 30 Aug 2026 22:20:18 +0200 Subject: [PATCH 3/4] token-fundraiser: derive the contributor PDA bump on-chain contribute took the contributor bump from instruction data and fed it to create_program_address. Several bumps can yield a valid address for the same seeds, so a contributor could open extra, non-canonical records for themselves and be metered against the per-contributor cap separately on each. Those records were also unrefundable, since refund only ever derives the canonical address. The bump is now derived on-chain with find_program_address and the supplied byte is gone from the instruction data entirely, so there is no longer a caller-controlled input to the derivation. This matches the Anchor version, whose `seeds = [...], bump` constraint likewise derives canonically rather than accepting a bump from the client. Adds a test that a contributor_account which is not the canonical PDA is refused before any record is created. --- .../program/src/instructions/contribute.rs | 18 ++++--- .../token-fundraiser/pinocchio/tests/test.ts | 53 +++++++++++++++---- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs index 46b87e73b..ddfaa89e2 100644 --- a/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs +++ b/tokens/token-fundraiser/pinocchio/program/src/instructions/contribute.rs @@ -27,7 +27,7 @@ use crate::{ /// 6. `[]` token program /// 7. `[]` system program /// -/// Instruction data: `[amount: u64 (LE), contributor_bump: u8]` +/// Instruction data: `[amount: u64 (LE)]` pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult { let [contributor, mint_to_raise, fundraiser, contributor_account, contributor_ata, vault, _token_program, _system_program] = accounts @@ -40,7 +40,6 @@ pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8 } let amount = read_u64(data, 0)?; - let contributor_bump = *data.get(8).ok_or(ProgramError::InvalidInstructionData)?; let mut state = Fundraiser::deserialize(&fundraiser.try_borrow()?)?; @@ -76,6 +75,16 @@ pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8 // Bind the contributor record to this signer before trusting it, whether it // already exists or is about to be created. + // + // The bump is derived here rather than taken from the caller: several bumps + // can yield a valid address for the same seeds, so accepting one would let a + // contributor open a second, non-canonical record and be metered against the + // cap separately on each. `refund` only ever derives the canonical address, + // so those extra records would also be unrefundable. + let (contributor_pda, contributor_bump) = Address::find_program_address( + &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref()], + program_id, + ); let bump_bytes = [contributor_bump]; let seeds = [ Seed::from(Contributor::SEED_PREFIX), @@ -83,11 +92,6 @@ pub fn contribute(program_id: &Address, accounts: &mut [AccountView], data: &[u8 Seed::from(contributor.address().as_ref()), Seed::from(&bump_bytes), ]; - let contributor_pda = Address::create_program_address( - &[Contributor::SEED_PREFIX, fundraiser.address().as_ref(), contributor.address().as_ref(), &bump_bytes], - program_id, - ) - .map_err(|_| ProgramError::InvalidSeeds)?; if contributor_account.address() != &contributor_pda { return Err(ProgramError::InvalidSeeds); } diff --git a/tokens/token-fundraiser/pinocchio/tests/test.ts b/tokens/token-fundraiser/pinocchio/tests/test.ts index 14305de9f..4c1c87bc4 100644 --- a/tokens/token-fundraiser/pinocchio/tests/test.ts +++ b/tokens/token-fundraiser/pinocchio/tests/test.ts @@ -176,7 +176,6 @@ describe('Token Fundraiser (Pinocchio)', () => { contributorAccount: Address, contributorAta: Address, vault: Address, - bump: number, amount: bigint, ) { return { @@ -191,7 +190,8 @@ describe('Token Fundraiser (Pinocchio)', () => { { address: TOKEN_PROGRAM_ADDRESS, role: AccountRole.READONLY }, { address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY }, ], - data: concatBytes(Uint8Array.of(1), u64(amount), Uint8Array.of(bump)), + // The contributor PDA bump is derived on-chain, not supplied here. + data: concatBytes(Uint8Array.of(1), u64(amount)), }; } @@ -261,7 +261,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const contributor = await generateKeyPairSigner(); svm.airdrop(contributor.address, lamports(1_000_000_000n)); const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); - const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + const [contributorAccount] = await contributorPda(fundraiser, contributor.address); send( await tx(contributor, [ @@ -272,7 +272,6 @@ describe('Token Fundraiser (Pinocchio)', () => { contributorAccount, contributorAta, vault, - cbump, 10n, ), ]), @@ -323,7 +322,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const contributor = await generateKeyPairSigner(); svm.airdrop(contributor.address, lamports(1_000_000_000n)); const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); - const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + const [contributorAccount] = await contributorPda(fundraiser, contributor.address); send( await tx(contributor, [ contributeIx( @@ -333,7 +332,6 @@ describe('Token Fundraiser (Pinocchio)', () => { contributorAccount, contributorAta, vault, - cbump, 10n, ), ]), @@ -373,10 +371,10 @@ describe('Token Fundraiser (Pinocchio)', () => { const victim = await generateKeyPairSigner(); svm.airdrop(victim.address, lamports(1_000_000_000n)); const victimAta = await fundAta(maker, maker, victim.address, mint.address, 1n); - const [victimAccount, victimBump] = await contributorPda(fundraiser, victim.address); + const [victimAccount] = await contributorPda(fundraiser, victim.address); send( await tx(victim, [ - contributeIx(victim, mint.address, fundraiser, victimAccount, victimAta, vault, victimBump, 1n), + contributeIx(victim, mint.address, fundraiser, victimAccount, victimAta, vault, 1n), ]), 'victim contribute', ); @@ -387,7 +385,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const attackerAta = await fundAta(maker, maker, attacker.address, mint.address, 1n); sendExpectingFailure( await tx(attacker, [ - contributeIx(attacker, mint.address, fundraiser, victimAccount, attackerAta, vault, victimBump, 1n), + contributeIx(attacker, mint.address, fundraiser, victimAccount, attackerAta, vault, 1n), ]), 'contribution into a substituted record', ); @@ -396,6 +394,40 @@ describe('Token Fundraiser (Pinocchio)', () => { assert.equal(tokenAmount(vault), 1n, 'vault only holds the recorded contribution'); }); + it('Rejects a contributor record that is not the canonical PDA', async () => { + const maker = await generateKeyPairSigner(); + svm.airdrop(maker.address, lamports(1_000_000_000n)); + const mint = await createMint(maker, 0, maker.address); + + const [fundraiser, bump] = await fundraiserPda(maker.address); + const [vault] = await findAssociatedTokenPda({ + owner: fundraiser, + mint: mint.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + send(await tx(maker, [initializeIx(maker, mint.address, fundraiser, vault, bump, 100n, 30)]), 'initialize'); + + // The record address is derived on-chain, so a contributor cannot open a + // second record for themselves at any other address — including one + // derived from a non-canonical bump. A record that is not the canonical + // PDA is refused before it can be created, which also keeps every record + // reachable by `refund` (which only ever derives the canonical address). + const contributor = await generateKeyPairSigner(); + svm.airdrop(contributor.address, lamports(1_000_000_000n)); + const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 5n); + const notTheCanonicalPda = (await generateKeyPairSigner()).address; + + sendExpectingFailure( + await tx(contributor, [ + contributeIx(contributor, mint.address, fundraiser, notTheCanonicalPda, contributorAta, vault, 5n), + ]), + 'contribution into a non-canonical record', + ); + + assert.equal(tokenAmount(contributorAta), 5n, 'contributor kept their tokens'); + assert.isNotOk(svm.getAccount(notTheCanonicalPda)?.exists, 'no second record was created'); + }); + it('Ignores unrecorded direct transfers into the vault', async () => { const maker = await generateKeyPairSigner(); svm.airdrop(maker.address, lamports(1_000_000_000n)); @@ -414,7 +446,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const contributor = await generateKeyPairSigner(); svm.airdrop(contributor.address, lamports(1_000_000_000n)); const contributorAta = await fundAta(maker, maker, contributor.address, mint.address, 10n); - const [contributorAccount, cbump] = await contributorPda(fundraiser, contributor.address); + const [contributorAccount] = await contributorPda(fundraiser, contributor.address); send( await tx(contributor, [ contributeIx( @@ -424,7 +456,6 @@ describe('Token Fundraiser (Pinocchio)', () => { contributorAccount, contributorAta, vault, - cbump, 10n, ), ]), From 48841e82669d6d6653491ef4eb08784a330bc81e Mon Sep 17 00:00:00 2001 From: MarkFeder Date: Sun, 30 Aug 2026 22:23:40 +0200 Subject: [PATCH 4/4] token-fundraiser: apply prettier formatting to the pinocchio tests --- .../token-fundraiser/pinocchio/tests/test.ts | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/tokens/token-fundraiser/pinocchio/tests/test.ts b/tokens/token-fundraiser/pinocchio/tests/test.ts index 4c1c87bc4..c114b9067 100644 --- a/tokens/token-fundraiser/pinocchio/tests/test.ts +++ b/tokens/token-fundraiser/pinocchio/tests/test.ts @@ -265,15 +265,7 @@ describe('Token Fundraiser (Pinocchio)', () => { send( await tx(contributor, [ - contributeIx( - contributor, - mint.address, - fundraiser, - contributorAccount, - contributorAta, - vault, - 10n, - ), + contributeIx(contributor, mint.address, fundraiser, contributorAccount, contributorAta, vault, 10n), ]), 'contribute', ); @@ -325,15 +317,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const [contributorAccount] = await contributorPda(fundraiser, contributor.address); send( await tx(contributor, [ - contributeIx( - contributor, - mint.address, - fundraiser, - contributorAccount, - contributorAta, - vault, - 10n, - ), + contributeIx(contributor, mint.address, fundraiser, contributorAccount, contributorAta, vault, 10n), ]), `contribute ${i}`, ); @@ -373,9 +357,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const victimAta = await fundAta(maker, maker, victim.address, mint.address, 1n); const [victimAccount] = await contributorPda(fundraiser, victim.address); send( - await tx(victim, [ - contributeIx(victim, mint.address, fundraiser, victimAccount, victimAta, vault, 1n), - ]), + await tx(victim, [contributeIx(victim, mint.address, fundraiser, victimAccount, victimAta, vault, 1n)]), 'victim contribute', ); @@ -449,15 +431,7 @@ describe('Token Fundraiser (Pinocchio)', () => { const [contributorAccount] = await contributorPda(fundraiser, contributor.address); send( await tx(contributor, [ - contributeIx( - contributor, - mint.address, - fundraiser, - contributorAccount, - contributorAta, - vault, - 10n, - ), + contributeIx(contributor, mint.address, fundraiser, contributorAccount, contributorAta, vault, 10n), ]), 'contribute', );