diff --git a/.github/actions/solana/install-solana/action.yaml b/.github/actions/solana/install-solana/action.yaml new file mode 100644 index 000000000..4c5679c87 --- /dev/null +++ b/.github/actions/solana/install-solana/action.yaml @@ -0,0 +1,33 @@ +name: "Install Solana" +description: "Installs Solana CLI tools at a specified version" +inputs: + solana-version: + description: "Solana version to install (e.g., v2.2.16)" + required: true + +runs: + using: "composite" + steps: + - name: Cache Solana CLI + id: cache-solana + uses: actions/cache@v4 + with: + path: ~/.local/share/solana/install + key: solana-${{ inputs.solana-version }}-${{ runner.os }} + + - name: Install Solana CLI + if: steps.cache-solana.outputs.cache-hit != 'true' + shell: bash + run: | + sh -c "$(curl -sSfL https://release.anza.xyz/${{ inputs.solana-version }}/install)" + + - name: Add Solana to PATH + shell: bash + run: | + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + + - name: Verify Solana Installation + shell: bash + run: | + export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH" + solana --version diff --git a/.github/workflows/trident-fuzz.yaml b/.github/workflows/trident-fuzz.yaml new file mode 100644 index 000000000..1d35afaa8 --- /dev/null +++ b/.github/workflows/trident-fuzz.yaml @@ -0,0 +1,105 @@ +name: Trident Fuzz Tests + +on: + workflow_dispatch: + pull_request: + branches: + - develop + - master + +env: + CARGO_TERM_COLOR: always + SOLANA_VERSION: "v3.1.9" + TRIDENT_VERSION: "0.13.0-rc.2" + +jobs: + build: + name: Build Programs + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - name: Install system packages + run: sudo apt-get update && sudo apt-get install -y build-essential libudev-dev protobuf-compiler libprotobuf-dev + shell: bash + + - name: Install Solana + uses: ./.github/actions/solana/install-solana + with: + solana-version: ${{ env.SOLANA_VERSION }} + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + shared-key: "trident-build" + + - name: Build All Programs + run: make build-all + + - name: Upload Program Artifacts + uses: actions/upload-artifact@v4 + with: + name: program-binaries + path: target/deploy/*.so + retention-days: 1 + + fuzz-tests: + name: Fuzz - ${{ matrix.test.name }} + needs: build + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + test: + - name: "fuzz_0" + target: "fuzz_0" + - name: "fuzz_1" + target: "fuzz_1" + - name: "fuzz_2" + target: "fuzz_2" + - name: "fuzz_3" + target: "fuzz_3" + - name: "fuzz_launchpad" + target: "fuzz_launchpad" + - name: "fuzz_pbpp" + target: "fuzz_pbpp" + - name: "fuzz_mint_governor" + target: "fuzz_mint_governor" + + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - name: Install system packages + run: sudo apt-get update && sudo apt-get install -y build-essential libudev-dev protobuf-compiler libprotobuf-dev + shell: bash + + - name: Download Program Artifacts + uses: actions/download-artifact@v4 + with: + name: program-binaries + path: target/deploy + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + shared-key: "trident-fuzz-${{ matrix.test.target }}" + workspaces: | + ./trident-tests + + - name: Install Trident + run: cargo install trident-cli --version ${{ env.TRIDENT_VERSION }} + + - name: Run Fuzz Test + working-directory: trident-tests + run: trident fuzz run ${{ matrix.test.target }} -e invariants + + checks: + name: Fuzz Tests (Checks) + needs: fuzz-tests + runs-on: ubuntu-24.04 + steps: + - run: echo "All fuzz tests completed successfully" diff --git a/Cargo.lock b/Cargo.lock index 8a51feafc..63c3c8989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aead" @@ -44,19 +44,19 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "version_check", "zerocopy 0.7.35", @@ -64,13 +64,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anchor-attribute-access-control" version = "0.29.0" @@ -196,7 +202,7 @@ dependencies = [ "bincode", "borsh 0.10.4", "bytemuck", - "getrandom 0.2.16", + "getrandom 0.2.17", "solana-program", "thiserror", ] @@ -212,7 +218,7 @@ dependencies = [ "solana-program", "spl-associated-token-account", "spl-token", - "spl-token-2022", + "spl-token-2022 0.9.0", ] [[package]] @@ -235,9 +241,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ark-bn254" @@ -429,11 +435,11 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -447,15 +453,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures", "digest 0.10.7", ] @@ -591,9 +598,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bv" @@ -607,22 +614,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -633,10 +640,11 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -644,15 +652,15 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "num-traits", ] @@ -697,9 +705,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "cpufeatures" @@ -812,7 +820,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -823,7 +831,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -929,12 +937,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futarchy" version = "0.6.1" @@ -949,9 +969,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "serde", "typenum", @@ -973,9 +993,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -986,14 +1006,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] @@ -1011,14 +1031,25 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", ] [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1070,9 +1101,9 @@ dependencies = [ [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "ident_case" @@ -1098,12 +1129,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.1", ] [[package]] @@ -1117,25 +1148,25 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -1143,9 +1174,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] @@ -1154,7 +1185,7 @@ dependencies = [ name = "launchpad" version = "0.6.1" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", "anchor-lang", "anchor-spl", "damm_v2_cpi", @@ -1171,7 +1202,7 @@ dependencies = [ name = "launchpad_v7" version = "0.7.0" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", "anchor-lang", "anchor-spl", "bid_wall", @@ -1193,9 +1224,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libsecp256k1" @@ -1259,25 +1290,24 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" @@ -1360,7 +1390,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1392,11 +1422,12 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ - "num_enum_derive 0.7.0", + "num_enum_derive 0.7.5", + "rustversion", ] [[package]] @@ -1408,19 +1439,19 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "num_enum_derive" -version = "0.7.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1437,9 +1468,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1447,15 +1478,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-link", ] [[package]] @@ -1484,9 +1515,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "performance_package_v2" @@ -1517,7 +1548,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.26", + "zerocopy 0.8.40", ] [[package]] @@ -1545,14 +1576,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1574,14 +1614,14 @@ checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1651,7 +1691,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -1674,9 +1714,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1684,9 +1724,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1694,18 +1734,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1715,9 +1755,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1726,9 +1766,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustc-hash" @@ -1747,15 +1787,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "scopeguard" @@ -1765,49 +1799,61 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.17" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -1829,7 +1875,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1912,7 +1958,7 @@ version = "1.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2c5e5dde22cac045d29675b3fefa84817e1f63b0b911d094c599e80c0c07d9" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", "blake3", "block-buffer 0.10.4", "bs58 0.4.0", @@ -1945,7 +1991,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1982,7 +2028,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.16", + "getrandom 0.2.17", "itertools", "js-sys", "lazy_static", @@ -2077,14 +2123,17 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "solana-security-txt" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" +checksum = "156bb61a96c605fa124e052d630dba2f6fb57e08c7d15b757e1e958b3ed7b3fe" +dependencies = [ + "hashbrown 0.15.2", +] [[package]] name = "solana-zk-token-sdk" @@ -2117,9 +2166,9 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3" +checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" dependencies = [ "assert_matches", "borsh 0.10.4", @@ -2127,7 +2176,7 @@ dependencies = [ "num-traits", "solana-program", "spl-token", - "spl-token-2022", + "spl-token-2022 1.0.0", "thiserror", ] @@ -2150,7 +2199,7 @@ checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2162,7 +2211,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.104", + "syn 2.0.117", "thiserror", ] @@ -2210,7 +2259,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2227,6 +2276,20 @@ dependencies = [ "spl-type-length-value", ] +[[package]] +name = "spl-tlv-account-resolution" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", +] + [[package]] name = "spl-token" version = "4.0.0" @@ -2252,18 +2315,55 @@ dependencies = [ "bytemuck", "num-derive 0.4.2", "num-traits", - "num_enum 0.7.0", + "num_enum 0.7.5", "solana-program", "solana-zk-token-sdk", "spl-memo", "spl-pod", "spl-token", "spl-token-metadata-interface", - "spl-transfer-hook-interface", + "spl-transfer-hook-interface 0.3.0", "spl-type-length-value", "thiserror", ] +[[package]] +name = "spl-token-2022" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.2", + "num-traits", + "num_enum 0.7.5", + "solana-program", + "solana-security-txt", + "solana-zk-token-sdk", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-transfer-hook-interface 0.4.1", + "spl-type-length-value", + "thiserror", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", +] + [[package]] name = "spl-token-metadata-interface" version = "0.2.0" @@ -2290,7 +2390,23 @@ dependencies = [ "spl-discriminator", "spl-pod", "spl-program-error", - "spl-tlv-account-resolution", + "spl-tlv-account-resolution 0.4.0", + "spl-type-length-value", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aabdb7c471566f6ddcee724beb8618449ea24b399e58d464d6b5bc7db550259" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution 0.5.1", "spl-type-length-value", ] @@ -2343,9 +2459,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2378,7 +2494,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2402,9 +2518,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -2430,6 +2546,15 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -2437,27 +2562,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", - "toml_datetime", - "winnow", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow 0.7.14", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] @@ -2507,45 +2653,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.104", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2553,31 +2686,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.104", - "wasm-bindgen-backend", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -2601,9 +2734,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys", ] @@ -2615,78 +2748,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.59.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winnow" version = "0.5.40" @@ -2697,14 +2772,20 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "winnow" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ - "bitflags", + "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "zerocopy" version = "0.7.35" @@ -2716,11 +2797,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ - "zerocopy-derive 0.8.26", + "zerocopy-derive 0.8.40", ] [[package]] @@ -2731,18 +2812,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2756,11 +2837,17 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..ec3040e2f --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: build-performance-package-v2 build-mint-governor build-bid-wall build-v06-launchpad build-futarchy build-price-based-performance-package build-v07-launchpad build-damm-v2-cpi build-conditional-vault build-all + +# Build individual programs +build-bid-wall: + cargo build-sbf --manifest-path programs/bid_wall/Cargo.toml --arch v2 + +build-futarchy: + cargo build-sbf --manifest-path programs/futarchy/Cargo.toml --arch v2 + +build-price-based-performance-package: + cargo build-sbf --manifest-path programs/price_based_performance_package/Cargo.toml --arch v2 + +build-v07-launchpad: + cargo build-sbf --manifest-path programs/v07_launchpad/Cargo.toml --arch v2 + +build-conditional-vault: + cargo build-sbf --manifest-path programs/conditional_vault/Cargo.toml --arch v2 + +build-mint-governor: + cargo build-sbf --manifest-path programs/mint_governor/Cargo.toml --arch v2 + +build-performance-package-v2: + cargo build-sbf --manifest-path programs/performance_package_v2/Cargo.toml --arch v2 + +# Build all programs +build-all: build-bid-wall build-futarchy build-price-based-performance-package build-v07-launchpad build-conditional-vault build-mint-governor build-performance-package-v2 + diff --git a/programs/v06_launchpad/Cargo.toml b/programs/v06_launchpad/Cargo.toml index cb6e19bdc..02b4a252c 100644 --- a/programs/v06_launchpad/Cargo.toml +++ b/programs/v06_launchpad/Cargo.toml @@ -26,7 +26,7 @@ price_based_performance_package = { path = "../price_based_performance_package", spl-memo = "=4.0.0" solana-program = "=1.17.14" spl-token = "=4.0.0" -ahash = "=0.8.6" +ahash = "=0.8.7" solana-security-txt = "1.1.1" squads-multisig-program = { git = "https://github.com/Squads-Protocol/v4", package = "squads-multisig-program", rev = "6d5235da621a2e9b7379ea358e48760e981053be", features = ["cpi"] } damm_v2_cpi = { path = "../damm_v2_cpi", features = ["cpi"] } diff --git a/programs/v07_launchpad/Cargo.toml b/programs/v07_launchpad/Cargo.toml index 556558adf..d87d249a6 100644 --- a/programs/v07_launchpad/Cargo.toml +++ b/programs/v07_launchpad/Cargo.toml @@ -26,7 +26,7 @@ price_based_performance_package = { path = "../price_based_performance_package", spl-memo = "=4.0.0" solana-program = "=1.17.14" spl-token = "=4.0.0" -ahash = "=0.8.6" +ahash = "=0.8.7" solana-security-txt = "1.1.1" squads-multisig-program = { git = "https://github.com/Squads-Protocol/v4", package = "squads-multisig-program", rev = "6d5235da621a2e9b7379ea358e48760e981053be", features = ["cpi"] } damm_v2_cpi = { path = "../damm_v2_cpi", features = ["cpi"] } diff --git a/trident-tests/.rustfmt.toml b/trident-tests/.rustfmt.toml new file mode 100644 index 000000000..74c8af673 --- /dev/null +++ b/trident-tests/.rustfmt.toml @@ -0,0 +1 @@ +imports_granularity = "Item" \ No newline at end of file diff --git a/trident-tests/Cargo.lock b/trident-tests/Cargo.lock new file mode 100644 index 000000000..22a7008b4 --- /dev/null +++ b/trident-tests/Cargo.lock @@ -0,0 +1,8413 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "agave-feature-set" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a2c365c0245cbb8959de725fc2b44c754b673fdf34c9a7f9d4a25c35a7bf1" +dependencies = [ + "ahash", + "solana-epoch-schedule", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", + "solana-svm-feature-set", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anchor-attribute-access-control" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a883ca44ef14b2113615fc6d3a85fefc68b5002034e88db37f7f1f802f88aa9" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c4d97763b29030412b4b80715076377edc9cc63bc3c9e667297778384b9fd2" +dependencies = [ + "anchor-syn", + "bs58", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae3328bbf9bbd517a51621b1ba6cbec06cbbc25e8cfc7403bddf69bcf088206" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2398a6d9e16df1ee9d7d37d970a8246756de898c8dd16ef6bdbe4da20cf39a" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12758f4ec2f0e98d4d56916c6fe95cb23d74b8723dd902c762c5ef46ebe7b65" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7193b5af2649813584aae6e3569c46fd59616a96af2083c556b13136c3830f" +dependencies = [ + "anchor-lang-idl", + "anchor-syn", + "anyhow", + "bs58", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d332d1a13c0fca1a446de140b656e66110a5e8406977dcb6a41e5d6f323760b0" +dependencies = [ + "anchor-syn", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8656e4af182edaeae665fa2d2d7ee81148518b5bd0be9a67f2a381bb17da7d46" +dependencies = [ + "anchor-syn", + "borsh-derive-internal", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcff2a083560cd79817db07d89a4de39a2c4b2eaa00c1742cf0df49b25ff2bed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67d85d5376578f12d840c29ff323190f6eecd65b00a0b5f2b2f232751d049cc" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-serde", + "anchor-derive-space", + "base64 0.21.7", + "bincode", + "borsh 0.10.4", + "bytemuck", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-define-syscall", + "solana-feature-gate-interface", + "solana-instruction", + "solana-instructions-sysvar", + "solana-invoke", + "solana-loader-v3-interface 3.0.0", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "thiserror 1.0.69", +] + +[[package]] +name = "anchor-lang-idl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e8599d21995f68e296265aa5ab0c3cef582fd58afec014d01bd0bce18a4418" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck 0.3.3", + "serde", + "serde_json", + "sha2 0.10.9", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdf143115440fe621bdac3a29a1f7472e09f6cd82b2aa569429a0c13f103838" +dependencies = [ + "anyhow", + "serde", +] + +[[package]] +name = "anchor-spl" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3397ab3fc5b198bbfe55d827ff58bd69f2a8d3f9f71c3732c23c2093fec4d3ef" +dependencies = [ + "anchor-lang", + "spl-associated-token-account", + "spl-pod", + "spl-token", + "spl-token-2022", + "spl-token-group-interface", + "spl-token-metadata-interface", +] + +[[package]] +name = "anchor-syn" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93b69aa7d099b59378433f6d7e20e1008fc10c69e48b220270e5b3f2ec4c8be" +dependencies = [ + "anyhow", + "bs58", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "syn 1.0.109", + "thiserror 1.0.69", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-compression" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" +dependencies = [ + "borsh-derive 0.10.4", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive 1.6.0", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "caps" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1ddba47aba30b6a889298ad0109c3b8dcb0e8fc993b459daa7067d46f865e0" +dependencies = [ + "libc", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.2", + "windows-sys 0.59.0", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.6", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.9", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "fastbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" +dependencies = [ + "getrandom 0.3.4", + "libm", + "rand 0.9.2", + "siphasher 1.0.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "fehler" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5729fe49ba028cd550747b6e62cd3d841beccab5390aa398538c31a2d983635" +dependencies = [ + "fehler-macros", +] + +[[package]] +name = "fehler-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb5acb1045ebbfa222e2c50679e392a71dd77030b78fb0189f2d9c5974400f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "five8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b8549488b4715defcb0d8a8a1c1c76a80661b5fa106b4ca0e7fce59d7d875" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_const" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dec3da8bc3ef08f2c04f61eab298c3ab334523e55f076354d6d6f613799a7b" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2551bf44bc5f776c15044b9b94153a00198be06743e262afaaa61f11ac7523a5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "fuzz_tests" +version = "0.1.0" +dependencies = [ + "borsh 1.6.0", + "squads-multisig", + "trident-fuzz", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.4.0", + "hyper 1.8.1", + "hyper-util", + "rustls 0.23.37", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots 1.0.6", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width 0.2.2", + "web-time", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi 0.5.2", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine 4.6.7", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "kaigan" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba15de5aeb137f0f65aa3bf82187647f1285abfe5b20c80c2c37f7007ad519a" +dependencies = [ + "borsh 0.10.4", + "serde", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "libc", +] + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "light-poseidon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +dependencies = [ + "ark-bn254", + "ark-ff", + "num-bigint 0.4.6", + "thiserror 1.0.69", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "percentage" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "prettytable" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46480520d1b77c9a3482d39939fcf96831537a250ec62d4fd8fbdf8e0302e781" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width 0.1.14", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml 0.5.11", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.3+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "qualifier_attr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.37", + "socket2 0.6.2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "fastbloom", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.37", + "rustls-pki-types", + "rustls-platform-verifier", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.37", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.6", +] + +[[package]] +name = "reqwest-middleware" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +dependencies = [ + "anyhow", + "async-trait", + "http 1.4.0", + "reqwest 0.12.28", + "serde", + "thiserror 1.0.69", + "tower-service", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.9", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.37", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.9", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +dependencies = [ + "serde_core", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "solana-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f949fe4edaeaea78c844023bfc1c898e0b1f5a100f8a8d2d0f85d0a7b090258" +dependencies = [ + "bincode", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-sysvar", +] + +[[package]] +name = "solana-account-decoder-client-types" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5519e8343325b707f17fbed54fcefb325131b692506d0af9e08a539d15e4f8cf" +dependencies = [ + "base64 0.22.1", + "bs58", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-pubkey", + "zstd", +] + +[[package]] +name = "solana-account-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" +dependencies = [ + "bincode", + "serde", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", +] + +[[package]] +name = "solana-address-lookup-table-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673f67efe870b64a65cb39e6194be5b26527691ce5922909939961a6e6b395" +dependencies = [ + "bincode", + "bytemuck", + "serde", + "serde_derive", + "solana-clock", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-slot-hashes", +] + +[[package]] +name = "solana-atomic-u64" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52e52720efe60465b052b9e7445a01c17550666beec855cce66f44766697bc2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "solana-big-mod-exp" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "solana-define-syscall", +] + +[[package]] +name = "solana-bincode" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" +dependencies = [ + "bincode", + "serde", + "solana-instruction", +] + +[[package]] +name = "solana-blake3-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" +dependencies = [ + "blake3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-bn254" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "bytemuck", + "solana-define-syscall", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-borsh" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718333bcd0a1a7aed6655aa66bef8d7fb047944922b2d3a18f49cbc13e73d004" +dependencies = [ + "borsh 0.10.4", + "borsh 1.6.0", +] + +[[package]] +name = "solana-bpf-loader-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aec57dcd80d0f6879956cad28854a6eebaed6b346ce56908ea01a9f36ab259" +dependencies = [ + "bincode", + "libsecp256k1", + "num-traits", + "qualifier_attr", + "scopeguard", + "solana-account", + "solana-account-info", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-bn254", + "solana-clock", + "solana-cpi", + "solana-curve25519", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-log-collector", + "solana-measure", + "solana-packet", + "solana-poseidon", + "solana-program-entrypoint", + "solana-program-runtime", + "solana-pubkey", + "solana-sbpf", + "solana-sdk-ids", + "solana-secp256k1-recover", + "solana-sha256-hasher", + "solana-stable-layout", + "solana-svm-feature-set", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-timings", + "solana-transaction-context", + "solana-type-overrides", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-builtins" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d61a31b63b52b0d268cbcd56c76f50314867d7f8e07a0f2c62ee7c9886e07b2" +dependencies = [ + "agave-feature-set", + "solana-bpf-loader-program", + "solana-compute-budget-program", + "solana-hash", + "solana-loader-v4-program", + "solana-program-runtime", + "solana-pubkey", + "solana-sdk-ids", + "solana-stake-program", + "solana-system-program", + "solana-vote-program", + "solana-zk-elgamal-proof-program", + "solana-zk-token-proof-program", +] + +[[package]] +name = "solana-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc55d1f263e0be4127daf33378d313ea0977f9ffd3fba50fa544ca26722fc695" +dependencies = [ + "async-trait", + "bincode", + "dashmap", + "futures", + "futures-util", + "indexmap", + "indicatif", + "log", + "quinn", + "rayon", + "solana-account", + "solana-client-traits", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-measure", + "solana-message", + "solana-pubkey", + "solana-pubsub-client", + "solana-quic-client", + "solana-quic-definitions", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-rpc-client-nonce-utils", + "solana-signature", + "solana-signer", + "solana-streamer", + "solana-thin-client", + "solana-time-utils", + "solana-tpu-client", + "solana-transaction", + "solana-transaction-error", + "solana-udp-client", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "solana-client-traits" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0071874e629f29e0eb3dab8a863e98502ac7aba55b7e0df1803fc5cac72a7" +dependencies = [ + "solana-account", + "solana-commitment-config", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction", + "solana-transaction-error", +] + +[[package]] +name = "solana-clock" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8584296123df8fe229b95e2ebfd37ae637fe9db9b7d4dd677ac5a78e80dbfce" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-cluster-type" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ace9fea2daa28354d107ea879cff107181d85cd4e0f78a2bedb10e1a428c97e" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", +] + +[[package]] +name = "solana-commitment-config" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac49c4dde3edfa832de1697e9bcdb7c3b3f7cb7a1981b7c62526c8bb6700fb73" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-compute-budget" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f4fc63bc2276a1618ca0bfc609da7448534ecb43a1cb387cdf9eaa2dc7bc272" +dependencies = [ + "solana-fee-structure", + "solana-program-runtime", +] + +[[package]] +name = "solana-compute-budget-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8432d2c4c22d0499aa06d62e4f7e333f81777b3d7c96050ae9e5cb71a8c3aee4" +dependencies = [ + "borsh 1.6.0", + "serde", + "serde_derive", + "solana-instruction", + "solana-sdk-ids", +] + +[[package]] +name = "solana-compute-budget-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072b02beed1862c6b7b7a8a699379594c4470a9371c711856a0a3c266dcf57e5" +dependencies = [ + "solana-program-runtime", +] + +[[package]] +name = "solana-config-program-client" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53aceac36f105fd4922e29b4f0c1f785b69d7b3e7e387e384b8985c8e0c3595e" +dependencies = [ + "bincode", + "borsh 0.10.4", + "kaigan", + "serde", + "solana-program", +] + +[[package]] +name = "solana-connection-cache" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c1cff5ebb26aefff52f1a8e476de70ec1683f8cc6e4a8c86b615842d91f436" +dependencies = [ + "async-trait", + "bincode", + "crossbeam-channel", + "futures-util", + "indexmap", + "log", + "rand 0.8.5", + "rayon", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-time-utils", + "solana-transaction-error", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-stable-layout", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae4261b9a8613d10e77ac831a8fa60b6fa52b9b103df46d641deff9f9812a23" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "solana-define-syscall", + "subtle", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-decode-error" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c781686a18db2f942e70913f7ca15dc120ec38dcab42ff7557db2c70c625a35" +dependencies = [ + "num-traits", +] + +[[package]] +name = "solana-define-syscall" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" + +[[package]] +name = "solana-derivation-path" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939756d798b25c5ec3cca10e06212bdca3b1443cb9bb740a38124f58b258737b" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + +[[package]] +name = "solana-ed25519-program" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feafa1691ea3ae588f99056f4bdd1293212c7ece28243d7da257c443e84753" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "ed25519-dalek", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + +[[package]] +name = "solana-epoch-info" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ef6f0b449290b0b9f32973eefd95af35b01c5c0c34c569f936c34c5b20d77b" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-epoch-rewards" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-rewards-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c5fd2662ae7574810904585fd443545ed2b568dbd304b25a31e79ccc76e81b" +dependencies = [ + "siphasher 0.3.11", + "solana-hash", + "solana-pubkey", +] + +[[package]] +name = "solana-epoch-schedule" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce071fbddecc55d727b1d7ed16a629afe4f6e4c217bc8d00af3b785f6f67ed" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-example-mocks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84461d56cbb8bb8d539347151e0525b53910102e4bced875d49d5139708e39d3" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f5c5382b449e8e4e3016fb05e418c53d57782d8b5c30aa372fc265654b956d" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-feature-set" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b93971e289d6425f88e6e3cb6668c4b05df78b3c518c249be55ced8efd6b6d" +dependencies = [ + "ahash", + "lazy_static", + "solana-epoch-schedule", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-fee-calculator" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89bc408da0fb3812bc3008189d148b4d3e08252c79ad810b245482a3f70cd8d" +dependencies = [ + "log", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-fee-structure" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33adf673581c38e810bf618f745bf31b683a0a4a4377682e6aaac5d9a058dd4e" +dependencies = [ + "serde", + "serde_derive", + "solana-message", + "solana-native-token", +] + +[[package]] +name = "solana-genesis-config" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3725085d47b96d37fef07a29d78d2787fc89a0b9004c66eed7753d1e554989f" +dependencies = [ + "bincode", + "chrono", + "memmap2", + "serde", + "serde_derive", + "solana-account", + "solana-clock", + "solana-cluster-type", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-inflation", + "solana-keypair", + "solana-logger", + "solana-poh-config", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sha256-hasher", + "solana-shred-version", + "solana-signer", + "solana-time-utils", +] + +[[package]] +name = "solana-hard-forks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c28371f878e2ead55611d8ba1b5fb879847156d04edea13693700ad1a28baf" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-hash" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" +dependencies = [ + "borsh 1.6.0", + "bytemuck", + "bytemuck_derive", + "five8", + "js-sys", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-sanitize", + "wasm-bindgen", +] + +[[package]] +name = "solana-inflation" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23eef6a09eb8e568ce6839573e4966850e85e9ce71e6ae1a6c930c1c43947de3" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-instruction" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab5682934bd1f65f8d2c16f21cb532526fcc1a09f796e2cacdb091eee5774ad" +dependencies = [ + "bincode", + "borsh 1.6.0", + "getrandom 0.2.17", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "serde_json", + "solana-define-syscall", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" +dependencies = [ + "bitflags 2.11.0", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-serialize-utils", + "solana-sysvar-id", +] + +[[package]] +name = "solana-invoke" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f5693c6de226b3626658377168b0184e94e8292ff16e3d31d4766e65627565" +dependencies = [ + "solana-account-info", + "solana-define-syscall", + "solana-instruction", + "solana-program-entrypoint", + "solana-stable-layout", +] + +[[package]] +name = "solana-keccak-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" +dependencies = [ + "sha3", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + +[[package]] +name = "solana-keypair" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd3f04aa1a05c535e93e121a95f66e7dcccf57e007282e8255535d24bf1e98bb" +dependencies = [ + "ed25519-dalek", + "ed25519-dalek-bip32", + "five8", + "rand 0.7.3", + "solana-derivation-path", + "solana-pubkey", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "wasm-bindgen", +] + +[[package]] +name = "solana-last-restart-slot" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6360ac2fdc72e7463565cd256eedcf10d7ef0c28a1249d261ec168c1b55cdd" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ab08006dad78ae7cd30df8eea0539e207d08d91eaefb3e1d49a446e1c49654" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4be76cfa9afd84ca2f35ebc09f0da0f0092935ccdac0595d98447f259538c2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7162a05b8b0773156b443bccd674ea78bb9aa406325b467ea78c06c99a63a2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v4-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706a777242f1f39a83e2a96a2a6cb034cb41169c6ecbee2cf09cb873d9659e7e" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-loader-v4-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ab01855d851fa2fb6034b0d48de33d77d5c5f5fb4b0353d8e4a934cc03d48a" +dependencies = [ + "log", + "qualifier_attr", + "solana-account", + "solana-bincode", + "solana-bpf-loader-program", + "solana-instruction", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-log-collector", + "solana-measure", + "solana-packet", + "solana-program-runtime", + "solana-pubkey", + "solana-sbpf", + "solana-sdk-ids", + "solana-transaction-context", + "solana-type-overrides", +] + +[[package]] +name = "solana-log-collector" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d945b1cf5bf7cbd6f5b78795beda7376370c827640df43bb2a1c17b492dc106" +dependencies = [ + "log", +] + +[[package]] +name = "solana-logger" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8e777ec1afd733939b532a42492d888ec7c88d8b4127a5d867eb45c6eb5cd5" +dependencies = [ + "env_logger", + "lazy_static", + "libc", + "log", + "signal-hook", +] + +[[package]] +name = "solana-measure" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11dcd67cd2ae6065e494b64e861e0498d046d95a61cbbf1ae3d58be1ea0f42ed" + +[[package]] +name = "solana-message" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +dependencies = [ + "bincode", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-bincode", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-metrics" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0375159d8460f423d39e5103dcff6e07796a5ec1850ee1fcfacfd2482a8f34b5" +dependencies = [ + "crossbeam-channel", + "gethostname", + "log", + "reqwest 0.12.28", + "solana-cluster-type", + "solana-sha256-hasher", + "solana-time-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-msg" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-native-token" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61515b880c36974053dd499c0510066783f0cc6ac17def0c7ef2a244874cf4a9" + +[[package]] +name = "solana-net-utils" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a9e831d0f09bd92135d48c5bc79071bb59c0537b9459f1b4dec17ecc0558fa" +dependencies = [ + "anyhow", + "bincode", + "bytes", + "itertools 0.12.1", + "log", + "nix", + "rand 0.8.5", + "serde", + "serde_derive", + "socket2 0.5.10", + "solana-serde", + "tokio", + "url", +] + +[[package]] +name = "solana-nonce" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703e22eb185537e06204a5bd9d509b948f0066f2d1d814a6f475dafb3ddf1325" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-nonce-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde971a20b8dbf60144d6a84439dda86b5466e00e2843091fe731083cda614da" +dependencies = [ + "solana-account", + "solana-hash", + "solana-nonce", + "solana-sdk-ids", +] + +[[package]] +name = "solana-offchain-message" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b526398ade5dea37f1f147ce55dae49aa017a5d7326606359b0445ca8d946581" +dependencies = [ + "num_enum", + "solana-hash", + "solana-packet", + "solana-pubkey", + "solana-sanitize", + "solana-sha256-hasher", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-packet" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004f2d2daf407b3ec1a1ca5ec34b3ccdfd6866dd2d3c7d0715004a96e4b6d127" +dependencies = [ + "bincode", + "bitflags 2.11.0", + "cfg_eval", + "serde", + "serde_derive", + "serde_with", +] + +[[package]] +name = "solana-perf" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37192c0be5c222ca49dbc5667288c5a8bb14837051dd98e541ee4dad160a5da9" +dependencies = [ + "ahash", + "bincode", + "bv", + "bytes", + "caps", + "curve25519-dalek 4.1.3", + "dlopen2", + "fnv", + "libc", + "log", + "nix", + "rand 0.8.5", + "rayon", + "serde", + "solana-hash", + "solana-message", + "solana-metrics", + "solana-packet", + "solana-pubkey", + "solana-rayon-threadlimit", + "solana-sdk-ids", + "solana-short-vec", + "solana-signature", + "solana-time-utils", +] + +[[package]] +name = "solana-poh-config" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d650c3b4b9060082ac6b0efbbb66865089c58405bfb45de449f3f2b91eccee75" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-poseidon" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbac4eb90016eeb1d37fa36e592d3a64421510c49666f81020736611c319faff" +dependencies = [ + "ark-bn254", + "light-poseidon", + "solana-define-syscall", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-precompile-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d87b2c1f5de77dfe2b175ee8dd318d196aaca4d0f66f02842f80c852811f9f8" +dependencies = [ + "num-traits", + "solana-decode-error", +] + +[[package]] +name = "solana-precompiles" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e92768a57c652edb0f5d1b30a7d0bc64192139c517967c18600debe9ae3832" +dependencies = [ + "lazy_static", + "solana-ed25519-program", + "solana-feature-set", + "solana-message", + "solana-precompile-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-secp256r1-program", +] + +[[package]] +name = "solana-presigner" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a57a24e6a4125fc69510b6774cd93402b943191b6cddad05de7281491c90fe" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-program" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.4", + "borsh 1.6.0", + "bs58", + "bytemuck", + "console_error_panic_hook", + "console_log", + "getrandom 0.2.17", + "lazy_static", + "log", + "memoffset", + "num-bigint 0.4.6", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-address-lookup-table-interface", + "solana-atomic-u64", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-borsh", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-example-mocks", + "solana-feature-gate-interface", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-keccak-hasher", + "solana-last-restart-slot", + "solana-loader-v2-interface", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-message", + "solana-msg", + "solana-native-token", + "solana-nonce", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-recover", + "solana-serde-varint", + "solana-serialize-utils", + "solana-sha256-hasher", + "solana-short-vec", + "solana-slot-hashes", + "solana-slot-history", + "solana-stable-layout", + "solana-stake-interface", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-vote-interface", + "thiserror 2.0.18", + "wasm-bindgen", +] + +[[package]] +name = "solana-program-entrypoint" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ce041b1a0ed275290a5008ee1a4a6c48f5054c8a3d78d313c08958a06aedbd" +dependencies = [ + "solana-account-info", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-program-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" +dependencies = [ + "borsh 1.6.0", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-pubkey", +] + +[[package]] +name = "solana-program-memory" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +dependencies = [ + "solana-define-syscall", +] + +[[package]] +name = "solana-program-option" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc677a2e9bc616eda6dbdab834d463372b92848b2bfe4a1ed4e4b4adba3397d0" + +[[package]] +name = "solana-program-pack" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319f0ef15e6e12dc37c597faccb7d62525a509fec5f6975ecb9419efddeb277b" +dependencies = [ + "solana-program-error", +] + +[[package]] +name = "solana-program-runtime" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5653001e07b657c9de6f0417cf9add1cf4325903732c480d415655e10cc86704" +dependencies = [ + "base64 0.22.1", + "bincode", + "enum-iterator", + "itertools 0.12.1", + "log", + "percentage", + "rand 0.8.5", + "serde", + "solana-account", + "solana-clock", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-structure", + "solana-hash", + "solana-instruction", + "solana-last-restart-slot", + "solana-log-collector", + "solana-measure", + "solana-metrics", + "solana-program-entrypoint", + "solana-pubkey", + "solana-rent", + "solana-sbpf", + "solana-sdk-ids", + "solana-slot-hashes", + "solana-stable-layout", + "solana-svm-callback", + "solana-svm-feature-set", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", + "solana-timings", + "solana-transaction-context", + "solana-type-overrides", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-pubkey" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" +dependencies = [ + "borsh 0.10.4", + "borsh 1.6.0", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "five8", + "five8_const", + "getrandom 0.2.17", + "js-sys", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-decode-error", + "solana-define-syscall", + "solana-sanitize", + "solana-sha256-hasher", + "wasm-bindgen", +] + +[[package]] +name = "solana-pubsub-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18a7476e1d2e8df5093816afd8fffee94fbb6e442d9be8e6bd3e85f88ce8d5c" +dependencies = [ + "crossbeam-channel", + "futures-util", + "http 0.2.12", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-clock", + "solana-pubkey", + "solana-rpc-client-types", + "solana-signature", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-quic-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feb5f4a97494459c435aa56de810500cc24e22d0afc632990a8e54a07c05a4" +dependencies = [ + "async-lock", + "async-trait", + "futures", + "itertools 0.12.1", + "log", + "quinn", + "quinn-proto", + "rustls 0.23.37", + "solana-connection-cache", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-pubkey", + "solana-quic-definitions", + "solana-rpc-client-api", + "solana-signer", + "solana-streamer", + "solana-tls-utils", + "solana-transaction-error", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "solana-quic-definitions" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf0d4d5b049eb1d0c35f7b18f305a27c8986fc5c0c9b383e97adaa35334379e" +dependencies = [ + "solana-keypair", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cc2a4cae3ef7bb6346b35a60756d2622c297d5fa204f96731db9194c0dc75b" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "solana-rent" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1aea8fdea9de98ca6e8c2da5827707fb3842833521b528a713810ca685d2480" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-rent-collector" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127e6dfa51e8c8ae3aa646d8b2672bc4ac901972a338a9e1cd249e030564fb9d" +dependencies = [ + "serde", + "serde_derive", + "solana-account", + "solana-clock", + "solana-epoch-schedule", + "solana-genesis-config", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", +] + +[[package]] +name = "solana-rent-debits" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6f9113c6003492e74438d1288e30cffa8ccfdc2ef7b49b9e816d8034da18cd" +dependencies = [ + "solana-pubkey", + "solana-reward-info", +] + +[[package]] +name = "solana-reserved-account-keys" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b22ea19ca2a3f28af7cd047c914abf833486bf7a7c4a10fc652fff09b385b1" +dependencies = [ + "lazy_static", + "solana-feature-set", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-reward-info" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18205b69139b1ae0ab8f6e11cdcb627328c0814422ad2482000fa2ca54ae4a2f" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-rpc-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d3161ac0918178e674c1f7f1bfac40de3e7ed0383bd65747d63113c156eaeb" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bincode", + "bs58", + "futures", + "indicatif", + "log", + "reqwest 0.12.28", + "reqwest-middleware", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-account-decoder-client-types", + "solana-clock", + "solana-commitment-config", + "solana-epoch-info", + "solana-epoch-schedule", + "solana-feature-gate-interface", + "solana-hash", + "solana-instruction", + "solana-message", + "solana-pubkey", + "solana-rpc-client-api", + "solana-signature", + "solana-transaction", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-version", + "solana-vote-interface", + "tokio", +] + +[[package]] +name = "solana-rpc-client-api" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dbc138685c79d88a766a8fd825057a74ea7a21e1dd7f8de275ada899540fff7" +dependencies = [ + "anyhow", + "jsonrpc-core", + "reqwest 0.12.28", + "reqwest-middleware", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-clock", + "solana-rpc-client-types", + "solana-signer", + "solana-transaction-error", + "solana-transaction-status-client-types", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-rpc-client-nonce-utils" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f0ee41b9894ff36adebe546a110b899b0d0294b07845d8acdc73822e6af4b0" +dependencies = [ + "solana-account", + "solana-commitment-config", + "solana-hash", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-rpc-client", + "solana-sdk-ids", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-rpc-client-types" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea428a81729255d895ea47fba9b30fd4dacbfe571a080448121bd0592751676" +dependencies = [ + "base64 0.22.1", + "bs58", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-account-decoder-client-types", + "solana-clock", + "solana-commitment-config", + "solana-fee-calculator", + "solana-inflation", + "solana-pubkey", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-version", + "spl-generic-token", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-sanitize" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f1bc1357b8188d9c4a3af3fc55276e56987265eb7ad073ae6f8180ee54cecf" + +[[package]] +name = "solana-sbpf" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "474a2d95dc819898ded08d24f29642d02189d3e1497bbb442a92a3997b7eb55f" +dependencies = [ + "byteorder", + "combine 3.8.1", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "thiserror 2.0.18", + "winapi", +] + +[[package]] +name = "solana-sdk" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc0e4a7635b902791c44b6581bfb82f3ada32c5bc0929a64f39fe4bb384c86a" +dependencies = [ + "bincode", + "bs58", + "getrandom 0.1.16", + "js-sys", + "serde", + "serde_json", + "solana-account", + "solana-bn254", + "solana-client-traits", + "solana-cluster-type", + "solana-commitment-config", + "solana-compute-budget-interface", + "solana-decode-error", + "solana-derivation-path", + "solana-ed25519-program", + "solana-epoch-info", + "solana-epoch-rewards-hasher", + "solana-feature-set", + "solana-fee-structure", + "solana-genesis-config", + "solana-hard-forks", + "solana-inflation", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-native-token", + "solana-nonce-account", + "solana-offchain-message", + "solana-packet", + "solana-poh-config", + "solana-precompile-error", + "solana-precompiles", + "solana-presigner", + "solana-program", + "solana-program-memory", + "solana-pubkey", + "solana-quic-definitions", + "solana-rent-collector", + "solana-rent-debits", + "solana-reserved-account-keys", + "solana-reward-info", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-secp256k1-program", + "solana-secp256k1-recover", + "solana-secp256r1-program", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-serde", + "solana-serde-varint", + "solana-short-vec", + "solana-shred-version", + "solana-signature", + "solana-signer", + "solana-system-transaction", + "solana-time-utils", + "solana-transaction", + "solana-transaction-context", + "solana-transaction-error", + "solana-validator-exit", + "thiserror 2.0.18", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-ids" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" +dependencies = [ + "solana-pubkey", +] + +[[package]] +name = "solana-sdk-macro" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86280da8b99d03560f6ab5aca9de2e38805681df34e0bb8f238e69b29433b9df" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "solana-secp256k1-program" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19833e4bc21558fe9ec61f239553abe7d05224347b57d65c2218aeeb82d6149" +dependencies = [ + "bincode", + "digest 0.10.7", + "libsecp256k1", + "serde", + "serde_derive", + "sha3", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", + "solana-signature", +] + +[[package]] +name = "solana-secp256k1-recover" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" +dependencies = [ + "borsh 1.6.0", + "libsecp256k1", + "solana-define-syscall", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-secp256r1-program" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0ae46da3071a900f02d367d99b2f3058fe2e90c5062ac50c4f20cfedad8f0f" +dependencies = [ + "bytemuck", + "openssl", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "156bb61a96c605fa124e052d630dba2f6fb57e08c7d15b757e1e958b3ed7b3fe" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "solana-seed-derivable" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beb82b5adb266c6ea90e5cf3967235644848eac476c5a1f2f9283a143b7c97f" +dependencies = [ + "solana-derivation-path", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36187af2324f079f65a675ec22b31c24919cb4ac22c79472e85d819db9bbbc15" +dependencies = [ + "hmac 0.12.1", + "pbkdf2", + "sha2 0.10.9", +] + +[[package]] +name = "solana-serde" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1931484a408af466e14171556a47adaa215953c7f48b24e5f6b0282763818b04" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serde-varint" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7e155eba458ecfb0107b98236088c3764a09ddf0201ec29e52a0be40857113" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serialize-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" +dependencies = [ + "solana-instruction", + "solana-pubkey", + "solana-sanitize", +] + +[[package]] +name = "solana-sha256-hasher" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" +dependencies = [ + "sha2 0.10.9", + "solana-define-syscall", + "solana-hash", +] + +[[package]] +name = "solana-short-vec" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c54c66f19b9766a56fa0057d060de8378676cb64987533fa088861858fc5a69" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-shred-version" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd3db0461089d1ad1a78d9ba3f15b563899ca2386351d38428faa5350c60a98" +dependencies = [ + "solana-hard-forks", + "solana-hash", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-signature" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" +dependencies = [ + "ed25519-dalek", + "five8", + "rand 0.8.5", + "serde", + "serde-big-array", + "serde_derive", + "solana-sanitize", +] + +[[package]] +name = "solana-signer" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-transaction-error", +] + +[[package]] +name = "solana-slot-hashes" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-slot-history" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccc1b2067ca22754d5283afb2b0126d61eae734fc616d23871b0943b0d935e" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stable-layout" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "solana-stake-interface" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +dependencies = [ + "borsh 0.10.4", + "borsh 1.6.0", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stake-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500e9b9d11573f12de91e94f9c4459882cd5ffc692776af49b610d6fcc0b167f" +dependencies = [ + "agave-feature-set", + "bincode", + "log", + "solana-account", + "solana-bincode", + "solana-clock", + "solana-config-program-client", + "solana-genesis-config", + "solana-instruction", + "solana-log-collector", + "solana-native-token", + "solana-packet", + "solana-program-runtime", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-stake-interface", + "solana-sysvar", + "solana-transaction-context", + "solana-type-overrides", + "solana-vote-interface", +] + +[[package]] +name = "solana-streamer" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5643516e5206b89dd4bdf67c39815606d835a51a13260e43349abdb92d241b1d" +dependencies = [ + "async-channel", + "bytes", + "crossbeam-channel", + "dashmap", + "futures", + "futures-util", + "governor", + "histogram", + "indexmap", + "itertools 0.12.1", + "libc", + "log", + "nix", + "pem", + "percentage", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rustls 0.23.37", + "smallvec", + "socket2 0.5.10", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-packet", + "solana-perf", + "solana-pubkey", + "solana-quic-definitions", + "solana-signature", + "solana-signer", + "solana-time-utils", + "solana-tls-utils", + "solana-transaction-error", + "solana-transaction-metrics-tracker", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "x509-parser", +] + +[[package]] +name = "solana-svm" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006180b920e8d8c1dab4f6a0fda248b5b97d912eda4c872534d178bc31231bec" +dependencies = [ + "ahash", + "log", + "percentage", + "serde", + "serde_derive", + "solana-account", + "solana-clock", + "solana-fee-structure", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-loader-v4-program", + "solana-log-collector", + "solana-measure", + "solana-message", + "solana-nonce", + "solana-nonce-account", + "solana-program-entrypoint", + "solana-program-pack", + "solana-program-runtime", + "solana-pubkey", + "solana-rent", + "solana-rent-collector", + "solana-rent-debits", + "solana-sdk-ids", + "solana-svm-callback", + "solana-svm-feature-set", + "solana-svm-rent-collector", + "solana-svm-transaction", + "solana-system-interface", + "solana-sysvar-id", + "solana-timings", + "solana-transaction-context", + "solana-transaction-error", + "solana-type-overrides", + "spl-generic-token", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-svm-callback" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cef9f7d5cfb5d375081a6c8ad712a6f0e055a15890081f845acf55d8254a7a2" +dependencies = [ + "solana-account", + "solana-precompile-error", + "solana-pubkey", +] + +[[package]] +name = "solana-svm-feature-set" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f24b836eb4d74ec255217bdbe0f24f64a07adeac31aca61f334f91cd4a3b1d5" + +[[package]] +name = "solana-svm-rent-collector" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030200d7f3ce4879f9d8c980ceb9e1d5e9a302866db035776496069b20c427b4" +dependencies = [ + "solana-account", + "solana-clock", + "solana-pubkey", + "solana-rent", + "solana-rent-collector", + "solana-sdk-ids", + "solana-transaction-context", + "solana-transaction-error", +] + +[[package]] +name = "solana-svm-transaction" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab717b9539375ebb088872c6c87d1d8832d19f30f154ecc530154d23f60a6f0c" +dependencies = [ + "solana-hash", + "solana-message", + "solana-pubkey", + "solana-sdk-ids", + "solana-signature", + "solana-transaction", +] + +[[package]] +name = "solana-system-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" +dependencies = [ + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-pubkey", + "wasm-bindgen", +] + +[[package]] +name = "solana-system-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ca36cef39aea7761be58d4108a56a2e27042fb1e913355fdb142a05fc7eab7" +dependencies = [ + "bincode", + "log", + "serde", + "serde_derive", + "solana-account", + "solana-bincode", + "solana-fee-calculator", + "solana-instruction", + "solana-log-collector", + "solana-nonce", + "solana-nonce-account", + "solana-packet", + "solana-program-runtime", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", + "solana-transaction-context", + "solana-type-overrides", +] + +[[package]] +name = "solana-system-transaction" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd98a25e5bcba8b6be8bcbb7b84b24c2a6a8178d7fb0e3077a916855ceba91a" +dependencies = [ + "solana-hash", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-signer", + "solana-system-interface", + "solana-transaction", +] + +[[package]] +name = "solana-sysvar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +dependencies = [ + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-define-syscall", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instructions-sysvar", + "solana-last-restart-slot", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", + "solana-rent", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sysvar-id" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" +dependencies = [ + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-thin-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c1025715a113e0e2e379b30a6bfe4455770dc0759dabf93f7dbd16646d5acbe" +dependencies = [ + "bincode", + "log", + "rayon", + "solana-account", + "solana-client-traits", + "solana-clock", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction", + "solana-transaction-error", +] + +[[package]] +name = "solana-time-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af261afb0e8c39252a04d026e3ea9c405342b08c871a2ad8aa5448e068c784c" + +[[package]] +name = "solana-timings" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c49b842dfc53c1bf9007eaa6730296dea93b4fce73f457ce1080af43375c0d6" +dependencies = [ + "eager", + "enum-iterator", + "solana-pubkey", +] + +[[package]] +name = "solana-tls-utils" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14494aa87a75a883d1abcfee00f1278a28ecc594a2f030084879eb40570728f6" +dependencies = [ + "rustls 0.23.37", + "solana-keypair", + "solana-pubkey", + "solana-signer", + "x509-parser", +] + +[[package]] +name = "solana-tpu-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17895ce70fd1dd93add3fbac87d599954ded93c63fa1c66f702d278d96a6da14" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap", + "indicatif", + "log", + "rayon", + "solana-client-traits", + "solana-clock", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-schedule", + "solana-measure", + "solana-message", + "solana-net-utils", + "solana-pubkey", + "solana-pubsub-client", + "solana-quic-definitions", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-signature", + "solana-signer", + "solana-transaction", + "solana-transaction-error", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "solana-transaction" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80657d6088f721148f5d889c828ca60c7daeedac9a8679f9ec215e0c42bcbf41" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-bincode", + "solana-feature-set", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-precompiles", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction-error", + "wasm-bindgen", +] + +[[package]] +name = "solana-transaction-context" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a312304361987a85b2ef2293920558e6612876a639dd1309daf6d0d59ef2fe" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account", + "solana-instruction", + "solana-instructions-sysvar", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", +] + +[[package]] +name = "solana-transaction-error" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" +dependencies = [ + "serde", + "serde_derive", + "solana-instruction", + "solana-sanitize", +] + +[[package]] +name = "solana-transaction-metrics-tracker" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fc4e1b6252dc724f5ee69db6229feb43070b7318651580d2174da8baefb993" +dependencies = [ + "base64 0.22.1", + "bincode", + "log", + "rand 0.8.5", + "solana-packet", + "solana-perf", + "solana-short-vec", + "solana-signature", +] + +[[package]] +name = "solana-transaction-status-client-types" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f1d7c2387c35850848212244d2b225847666cb52d3bd59a5c409d2c300303d" +dependencies = [ + "base64 0.22.1", + "bincode", + "bs58", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-commitment-config", + "solana-message", + "solana-reward-info", + "solana-signature", + "solana-transaction", + "solana-transaction-context", + "solana-transaction-error", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-type-overrides" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d80c44761eb398a157d809a04840865c347e1831ae3859b6100c0ee457bc1a" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "solana-udp-client" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd36227dd3035ac09a89d4239551d2e3d7d9b177b61ccc7c6d393c3974d0efa" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-keypair", + "solana-net-utils", + "solana-streamer", + "solana-transaction-error", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "solana-validator-exit" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbf6d7a3c0b28dd5335c52c0e9eae49d0ae489a8f324917faf0ded65a812c1d" + +[[package]] +name = "solana-version" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3324d46c7f7b7f5d34bf7dc71a2883bdc072c7b28ca81d0b2167ecec4cf8da9f" +dependencies = [ + "agave-feature-set", + "rand 0.8.5", + "semver", + "serde", + "serde_derive", + "solana-sanitize", + "solana-serde-varint", +] + +[[package]] +name = "solana-vote-interface" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +dependencies = [ + "bincode", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-decode-error", + "solana-hash", + "solana-instruction", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-serde-varint", + "solana-serialize-utils", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-vote-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908d0e72c8b83e48762eb3e8c9114497cf4b1d66e506e360c46aba9308e71299" +dependencies = [ + "agave-feature-set", + "bincode", + "log", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-account", + "solana-bincode", + "solana-clock", + "solana-epoch-schedule", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-packet", + "solana-program-runtime", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-signer", + "solana-slot-hashes", + "solana-transaction", + "solana-transaction-context", + "solana-vote-interface", + "thiserror 2.0.18", +] + +[[package]] +name = "solana-zk-elgamal-proof-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70cea14481d8efede6b115a2581f27bc7c6fdfba0752c20398456c3ac1245fc4" +dependencies = [ + "agave-feature-set", + "bytemuck", + "num-derive", + "num-traits", + "solana-instruction", + "solana-log-collector", + "solana-program-runtime", + "solana-sdk-ids", + "solana-zk-sdk", +] + +[[package]] +name = "solana-zk-sdk" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b9fc6ec37d16d0dccff708ed1dd6ea9ba61796700c3bb7c3b401973f10f63b" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "itertools 0.12.1", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.18", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-zk-token-proof-program" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579752ad6ea2a671995f13c763bf28288c3c895cb857a518cc4ebab93c9a8dde" +dependencies = [ + "agave-feature-set", + "bytemuck", + "num-derive", + "num-traits", + "solana-instruction", + "solana-log-collector", + "solana-program-runtime", + "solana-sdk-ids", + "solana-zk-token-sdk", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5055e5df94abd5badf4f947681c893375bdb6f8f543c05d2a7ab9647a6a9d205" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek 4.1.3", + "itertools 0.12.1", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-curve25519", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spl-associated-token-account" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae179d4a26b3c7a20c839898e6aed84cb4477adf108a366c95532f058aea041b" +dependencies = [ + "borsh 1.6.0", + "num-derive", + "num-traits", + "solana-program", + "spl-associated-token-account-client", + "spl-token", + "spl-token-2022", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-associated-token-account-client" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "spl-associated-token-account-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6bbe0794e532ac08428d3abf5bf8ae75bd81dfddd785c388e326c00c92c6f5" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "spl-discriminator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7398da23554a31660f17718164e31d31900956054f54f52d5ec1be51cb4f4b3" +dependencies = [ + "bytemuck", + "solana-program-error", + "solana-sha256-hasher", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn", + "syn 2.0.117", +] + +[[package]] +name = "spl-discriminator-syn" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.117", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-elgamal-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65edfeed09cd4231e595616aa96022214f9c9d2be02dea62c2b30d5695a6833a" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-cpi", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-system-interface", + "solana-sysvar", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction 0.3.0", +] + +[[package]] +name = "spl-generic-token" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741a62a566d97c58d33f9ed32337ceedd4e35109a686e31b1866c5dfa56abddc" +dependencies = [ + "bytemuck", + "solana-pubkey", +] + +[[package]] +name = "spl-memo" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" +dependencies = [ + "solana-account-info", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "spl-pod" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" +dependencies = [ + "borsh 1.6.0", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-program-error" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdebc8b42553070b75aa5106f071fef2eb798c64a7ec63375da4b1f058688c6" +dependencies = [ + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-program-error-derive", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2539e259c66910d78593475540e8072f0b10f0f61d7607bbf7593899ed52d0" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.117", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1408e961215688715d5a1063cbdcf982de225c45f99c82b4f7d7e1dd22b998d7" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-type-length-value", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053067c6a82c705004f91dae058b11b4780407e9ccd6799dc9e7d0fab5f242da" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sysvar", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-2022" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f0dfbb079eebaee55e793e92ca5f433744f4b71ee04880bfd6beefba5973e5" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-clock", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-native-token", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-security-txt", + "solana-system-interface", + "solana-sysvar", + "solana-zk-sdk", + "spl-elgamal-registry", + "spl-memo", + "spl-pod", + "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction 0.3.0", + "spl-token-confidential-transfer-proof-generation", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-transfer-hook-interface", + "spl-type-length-value", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-2022-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d7ae2ee6b856f8ddcbdc3b3a9f4d2141582bbe150f93e5298ee97e0251fa04" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction 0.4.1", + "spl-token-confidential-transfer-proof-generation", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-type-length-value", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddd52bfc0f1c677b41493dafa3f2dbbb4b47cf0990f08905429e19dc8289b35" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2629860ff04c17bafa9ba4bed8850a404ecac81074113e1f840dbd0ebb7bd6" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-curve25519", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512c85bdbbb4cbcc2038849a9e164c958b16541f252b53ea1a3933191c0a4a1a" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-curve25519", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa27b9174bea869a7ebf31e0be6890bce90b1a4288bc2bbf24bd413f80ae3fde" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5597b4cd76f85ce7cd206045b7dc22da8c25516573d42d267c8d1fd128db5129" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e0c2d4e38ef5834cf7fb1b592b8a8c6eab8485f5ac7a04a151b502c63a0aaa" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-instruction", + "solana-program-error", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304d6e06f0de0c13a621464b1fd5d4b1bebf60d15ca71a44d3839958e0da16ee" +dependencies = [ + "borsh 1.6.0", + "num-derive", + "num-traits", + "solana-borsh", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-type-length-value", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e905b849b6aba63bde8c4badac944ebb6c8e6e14817029cbe1bc16829133bd" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-cpi", + "solana-decode-error", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value", + "thiserror 2.0.18", +] + +[[package]] +name = "spl-type-length-value" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d417eb548214fa822d93f84444024b4e57c13ed6719d4dcc68eec24fb481e9f5" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.18", +] + +[[package]] +name = "squads-multisig" +version = "2.1.0" +source = "git+https://github.com/Squads-Protocol/v4?rev=05f23726a094b6a5a0d97eaccf22af72373ac9c6#05f23726a094b6a5a0d97eaccf22af72373ac9c6" +dependencies = [ + "futures", + "solana-message", + "solana-rpc-client", + "solana-rpc-client-api", + "squads-multisig-program", + "thiserror 1.0.69", +] + +[[package]] +name = "squads-multisig-program" +version = "2.1.0" +source = "git+https://github.com/Squads-Protocol/v4?rev=05f23726a094b6a5a0d97eaccf22af72373ac9c6#05f23726a094b6a5a0d97eaccf22af72373ac9c6" +dependencies = [ + "anchor-lang", + "anchor-spl", + "solana-address-lookup-table-interface", + "solana-borsh", + "solana-program", + "solana-security-txt", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.37", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", + "tungstenite", + "webpki-roots 0.25.4", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.25.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a07913e63758bc95142d9863a5a45173b71515e68b690cad70cf99c3255ce1" +dependencies = [ + "indexmap", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "async-compression", + "bitflags 2.11.0", + "bytes", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "iri-string", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trident-config" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab082ae00de1354bb5f4f0fa2f1774660f46cab3438ddcd76414b7f77bb2ec30" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bincode", + "fehler", + "rand 0.8.5", + "serde", + "serde_json", + "solana-client", + "solana-loader-v2-interface", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-sdk", + "thiserror 1.0.69", + "toml 0.8.23", +] + +[[package]] +name = "trident-derive-flow-executor" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d3009900503120bf82203740aab5e5ee0ff932110a21ada66d5b2ea2900f0f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "trident-syn", +] + +[[package]] +name = "trident-derive-fuzz-test-methods" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719dabc6485b7a449feda506a2f90bae1be036efd3f355cf06aadd6708125646" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "trident-syn", +] + +[[package]] +name = "trident-fuzz" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a05242635bd95f47be8c9046c810672330a7b64f1b93d32cc206664a3558178d" +dependencies = [ + "bincode", + "borsh 1.6.0", + "getrandom 0.3.4", + "hex", + "indicatif", + "itertools 0.14.0", + "rand 0.8.5", + "reqwest 0.11.27", + "serde", + "sha2 0.10.9", + "solana-loader-v3-interface 5.0.0", + "solana-sdk", + "spl-associated-token-account-interface", + "spl-pod", + "spl-token-2022-interface", + "spl-token-group-interface", + "spl-token-interface", + "spl-token-metadata-interface", + "thiserror 1.0.69", + "tokio", + "trident-config", + "trident-derive-flow-executor", + "trident-derive-fuzz-test-methods", + "trident-fuzz-metrics", + "trident-svm", +] + +[[package]] +name = "trident-fuzz-metrics" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3bd33ec51af38351d21bfa9d30796de987b0bd4d350c784b44d8441bdb5bf24" +dependencies = [ + "hex", + "prettytable", + "serde", + "serde_json", + "sha2 0.10.9", + "solana-sdk", +] + +[[package]] +name = "trident-svm" +version = "0.3.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e850c21b2c402bb75c2feca8468a1a7da8c263e8cf8831cdcfaeaa8415387a" +dependencies = [ + "bincode", + "log", + "serde", + "solana-account", + "solana-account-info", + "solana-bpf-loader-program", + "solana-builtins", + "solana-clock", + "solana-compute-budget", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-structure", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-loader-v3-interface 5.0.0", + "solana-logger", + "solana-program-runtime", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-signer", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-svm", + "solana-svm-callback", + "solana-svm-feature-set", + "solana-sysvar", + "solana-sysvar-id", + "solana-transaction", + "solana-transaction-error", +] + +[[package]] +name = "trident-syn" +version = "0.13.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04f17881f81eb2dcaaf3eb0d22b09cbe6de9fff11c7c2004cd4fffdafa7d277" +dependencies = [ + "petgraph", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 0.2.12", + "httparse", + "log", + "rand 0.8.5", + "rustls 0.21.12", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", + "webpki-roots 0.24.0", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.2", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki 0.101.7", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure 0.13.2", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure 0.13.2", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/trident-tests/Cargo.toml b/trident-tests/Cargo.toml new file mode 100644 index 000000000..4dbe88898 --- /dev/null +++ b/trident-tests/Cargo.toml @@ -0,0 +1,49 @@ +[workspace] + +[package] +name = "fuzz_tests" +version = "0.1.0" +description = "Created with Trident" +edition = "2021" + +[profile.release] +overflow-checks = true + +[dependencies] +borsh = "1.5.3" +squads-multisig = { git = "https://github.com/Squads-Protocol/v4", rev = "05f23726a094b6a5a0d97eaccf22af72373ac9c6" } + +[dependencies.trident-fuzz] +version = "0.13.0-rc.2" +features = ["token"] + +[lib] +path = "common/mod.rs" + +[[bin]] +name = "fuzz_0" +path = "fuzz_0/test_fuzz.rs" + +[[bin]] +name = "fuzz_1" +path = "fuzz_1/test_fuzz.rs" + +[[bin]] +name = "fuzz_2" +path = "fuzz_2/test_fuzz.rs" + +[[bin]] +name = "fuzz_3" +path = "fuzz_3/test_fuzz.rs" + +[[bin]] +name = "fuzz_launchpad" +path = "fuzz_launchpad/test_fuzz.rs" + +[[bin]] +name = "fuzz_pbpp" +path = "fuzz_pbpp/test_fuzz.rs" + +[[bin]] +name = "fuzz_mint_governor" +path = "fuzz_mint_governor/test_fuzz.rs" diff --git a/trident-tests/Makefile b/trident-tests/Makefile new file mode 100644 index 000000000..f6765e10b --- /dev/null +++ b/trident-tests/Makefile @@ -0,0 +1,4 @@ +format: + cargo +nightly fmt +clippy: + cargo clippy \ No newline at end of file diff --git a/trident-tests/README.md b/trident-tests/README.md new file mode 100644 index 000000000..8deb53023 --- /dev/null +++ b/trident-tests/README.md @@ -0,0 +1,66 @@ +# Fuzzing Tests + + +## How to run the tests: + + +### 1. Install trident-cli + +```bash +cargo install trident-cli --version 0.13.0-rc.2 +``` + +### 2. Build the programs + +```bash +# execute from project root +make build-all +``` + +> [!WARNING] +> Make sure the Solana-CLI version is 3.1.9. + +### 3. Run the tests + +```bash +trident fuzz run fuzz_0 +# or another fuzz test by name +``` + + +## Fuzz Test Suites + +### fuzz_0 - Liquidity Operations +Exercises randomized `provide_liquidity` and `withdraw_liquidity` flows for multiple users after the pool is seeded with initial liquidity. The suite is mainly looking for accounting bugs in LP minting/burning, reserve updates, and per-user position tracking. + +The invariants focus on ensuring deposits move assets from users into the pool, withdrawals move assets back out in the correct direction, LP position liquidity only changes consistently with the action taken, and no user can withdraw materially more than they have contributed apart from tiny rounding dust. + +### fuzz_1 - Spot Trading +Exercises repeated `spot_swap` buy and sell operations against a seeded spot pool. It is focused on catching incorrect reserve updates, fee accounting mistakes, and trader balance changes that move opposite to the intended swap direction. + +The invariants focus on swap-direction sanity for both the trader and the pool, plus end-of-iteration checks that on-chain spot reserves and protocol fee balances still reconcile with the DAO vault balances and that an empty pool cannot report non-zero reserves. + +### fuzz_2 - Conditional Trading (Simple Principle) +Sets up an active futarchy proposal with conditional vaults and then fuzzes `conditional_swap` operations across the pass and fail markets. Instead of re-implementing the AMM pricing logic off-chain, this suite is aimed at detecting leaks, broken split/merge behavior, and incorrect conditional reserve movement through conservation-style checks. + +The invariants focus on total underlying token conservation across tracked holders and vaults, pass/fail token parity for both base and quote legs, and simple trade-direction checks that the trader spends the input asset, receives the output asset, and moves the selected conditional pool reserves in the expected direction. + +### fuzz_3 - Mixed Spot + Conditional Trading (Proposal Active) +Keeps the DAO in active `PoolState::Futarchy` and mixes `spot_swap` and `conditional_swap` flows in the same run. This is the most integration-heavy futarchy suite: it is designed to catch accounting mismatches between the spot pool, conditional pools, and vault balances while both trading modes interact during an active proposal. + +The invariants focus on conservation of the underlying assets, pass/fail conditional token parity, directional sanity for both spot and conditional swaps, and aggregated vault-to-reserve alignment while the AMM is in futarchy mode. At the end of each run it also finalizes the proposal and checks that the system cleanly transitions back to spot mode with reserves and vault balances still aligned. + +### fuzz_launchpad - Launch Lifecycle +Exercises the main `launchpad` lifecycle with mostly-valid but occasionally-invalid actions across `initialize_launch`, `start_launch`, `fund`, `close_launch`, `set_funding_record_approval`, `complete_launch`, `refund`, `claim`, and the post-completion paths for additional token allocation and performance package initialization. The suite is focused on state machine safety, custody of deposited funds, and making sure launch progression cannot be bypassed by malformed account combinations or bad signers. + +The invariants focus on whether each instruction preserves the expected launch state transitions, whether funding records and approval amounts stay consistent with committed funds, whether successful completion/refund/claim paths pay out from the right vaults to the right parties, and whether optional post-launch distribution steps remain tied to the finalized launch state. + +### fuzz_pbpp - Price Based Performance Package +Exercises the full performance package flow: `initialize_performance_package`, `start_unlock`, `complete_unlock`, authority changes, and proposal/execution of package updates. The suite is focused on the package state machine around oracle-driven unlocking as well as the authority and recipient control paths that can mutate package configuration. + +The invariants focus on ensuring unlock only progresses when time and oracle conditions are satisfied, token release goes to the configured recipient path, administrative changes respect the expected authority/recipient permissions, and state transitions remain coherent as the package moves from locked to unlocking to completed or through approved configuration changes. + +### fuzz_mint_governor - Mint Authorization And Limits +Exercises a simplified `mint_governor` flow centered on adding, updating, removing, and using a single mint authority. The suite is focused on two things only: unauthorized access should fail, and minting should respect the configured `max_total` limit for the authorized minter. + +The invariants focus on keeping the tracked governor configuration stable after initialization and ensuring the tracked mint-authority PDA either does not exist when removed or matches the expected authorized minter, configured mint cap, and cumulative minted amount when active. diff --git a/trident-tests/Trident.toml b/trident-tests/Trident.toml new file mode 100644 index 000000000..31a2c9e24 --- /dev/null +++ b/trident-tests/Trident.toml @@ -0,0 +1,54 @@ +[fuzz.metrics] +enabled = true +dashboard = false + + +[[fuzz.programs]] +address = "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx" +program = "../target/deploy/bid_wall.so" + +[[fuzz.programs]] +address = "VLTX1ishMBbcX3rdBWGssxawAo1Q2X2qxYFYqiGodVg" +program = "../target/deploy/conditional_vault.so" + +[[fuzz.programs]] +address = "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq" +program = "../target/deploy/futarchy.so" + +[[fuzz.programs]] +address = "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM" +program = "../target/deploy/launchpad_v7.so" + +[[fuzz.programs]] +address = "pbPPQH7jyKoSLu8QYs3rSY3YkDRXEBojKbTgnUg7NDS" +program = "../target/deploy/price_based_performance_package.so" + +[[fuzz.programs]] +address = "gvnr27cVeyW3AVf3acL7VCJ5WjGAphytnsgcK1feHyH" +program = "../target/deploy/mint_governor.so" + + +# ============================== +# External Programs +# ============================== + +[[fuzz.fork]] +address = "BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr" +cluster = "m" + +[[fuzz.fork]] +address = "5DH2e3cJmFpyi6mk65EGFediunm4ui6BiKNUNrhWtD1b" +cluster = "m" + +[[fuzz.fork]] +address = "SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf" +cluster = "m" + +[[fuzz.fork]] +address = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG" +cluster = "m" + +[[fuzz.fork]] +address = "FaA6RM9enPh1tU9Y8LiGCq715JubLc49WGcYTdNvDfsc" +cluster = "m" + diff --git a/trident-tests/accounts/mainnet/meteora/damm-config.json b/trident-tests/accounts/mainnet/meteora/damm-config.json new file mode 100644 index 000000000..d734266ad --- /dev/null +++ b/trident-tests/accounts/mainnet/meteora/damm-config.json @@ -0,0 +1,14 @@ +{ + "pubkey": "FaA6RM9enPh1tU9Y8LiGCq715JubLc49WGcYTdNvDfsc", + "account": { + "lamports": 3173760, + "data": [ + "mwyq4B76zIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMLm86DiqLN9QjbB+uCN/aF66jA6p4q7ufPWg1/nbga3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAALYUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 328 + } +} \ No newline at end of file diff --git a/trident-tests/accounts/mainnet/squads/squads-program-config-treasury.json b/trident-tests/accounts/mainnet/squads/squads-program-config-treasury.json new file mode 100644 index 000000000..a0a8fb5bb --- /dev/null +++ b/trident-tests/accounts/mainnet/squads/squads-program-config-treasury.json @@ -0,0 +1,14 @@ +{ + "pubkey": "5DH2e3cJmFpyi6mk65EGFediunm4ui6BiKNUNrhWtD1b", + "account": { + "lamports": 2405101000133, + "data": [ + "", + "base64" + ], + "owner": "11111111111111111111111111111111", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 0 + } +} \ No newline at end of file diff --git a/trident-tests/accounts/mainnet/squads/squads-program-config.json b/trident-tests/accounts/mainnet/squads/squads-program-config.json new file mode 100644 index 000000000..7c65ed99a --- /dev/null +++ b/trident-tests/accounts/mainnet/squads/squads-program-config.json @@ -0,0 +1,14 @@ +{ + "pubkey": "BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr", + "account": { + "lamports": 1893120, + "data": [ + "xNJa55CVjD92Raz2HiWPdZHPcd7iP2i7V0ot8H6/wFdsT5Tc2zbKegAAAAAAAAAAPpPXMsRIJCeQ0tu1MaQKvRnxbFXjEyz/UnetydaTq1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 144 + } +} \ No newline at end of file diff --git a/trident-tests/common/conditional_vault.rs b/trident-tests/common/conditional_vault.rs new file mode 100644 index 000000000..e2e1674e3 --- /dev/null +++ b/trident-tests/common/conditional_vault.rs @@ -0,0 +1,202 @@ +use super::types::conditional_vault::InitializeConditionalVaultInstructionAccounts; +use super::types::conditional_vault::InitializeConditionalVaultInstructionData; +use super::types::conditional_vault::InitializeQuestionArgs; +use super::types::*; + +use trident_fuzz::fuzzing::*; + +use super::constants::*; + +use super::pda; +use super::token; + +pub fn initialize_question( + trident: &mut Trident, + payer: Pubkey, + args: InitializeQuestionArgs, + message: Option<&str>, +) -> Pubkey { + let question = pda::get_question_pda(trident, args.questionId, args.oracle, args.numOutcomes); + + let event_authority = pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let init_question = conditional_vault::InitializeQuestionInstruction::data( + conditional_vault::InitializeQuestionInstructionData::new(args), + ) + .accounts( + conditional_vault::InitializeQuestionInstructionAccounts::new( + question, + payer, + solana_sdk::system_program::ID, + event_authority, + conditional_vault::program_id(), + ), + ) + .instruction(); + + let res = trident.process_transaction(&[init_question], message); + + assert!(res.is_success()); + + question +} + +pub fn initialize_conditional_vault( + trident: &mut Trident, + payer: Pubkey, + question: Pubkey, + underlying_token_mint: Pubkey, + message: Option<&str>, +) -> Pubkey { + let vault = pda::get_conditional_vault_pda(trident, question, underlying_token_mint); + + let event_authority = pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let vault_underlying_token_account = + token::initialize_associated_token_account(trident, payer, underlying_token_mint, vault); + + let mut remaining_accounts = vec![]; + + let question_data = trident + .get_account_with_type::(&question, None) + .expect("Question not deserialized"); + + for x in 0..question_data.payoutNumerators.len() { + let conditional_token_mint_address = + pda::get_conditional_token_mint_pda(trident, vault, x as u8); + remaining_accounts.push(AccountMeta::new(conditional_token_mint_address, false)); + } + + let init_cond_vault = conditional_vault::InitializeConditionalVaultInstruction::data( + InitializeConditionalVaultInstructionData::new(), + ) + .accounts(InitializeConditionalVaultInstructionAccounts::new( + vault, + question, + underlying_token_mint, + vault_underlying_token_account, + payer, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + solana_sdk::system_program::ID, + event_authority, + conditional_vault::program_id(), + )) + .remaining_accounts(remaining_accounts) + .instruction(); + + let res = trident.process_transaction(&[init_cond_vault], message); + + assert!(res.is_success()); + + vault +} + +pub fn split_tokens( + trident: &mut Trident, + payer: Pubkey, + question: Pubkey, + vault: Pubkey, + amount: u64, + authority: Pubkey, + message: Option<&str>, +) { + let conditional_vault_data = trident + .get_account_with_type::(&vault, None) + .expect("Conditional vault not found"); + + let user_underlying_token_account = trident.get_associated_token_address( + &conditional_vault_data.underlyingTokenMint, + &authority, + &TOKEN_PROGRAM_ID, + ); + + let event_authority = pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let mut conditional_token_accounts = vec![]; + + for pubkey in &conditional_vault_data.conditionalTokenMints { + conditional_token_accounts.push(AccountMeta::new(*pubkey, false)); + } + + for pubkey in &conditional_vault_data.conditionalTokenMints { + let conditional_token_user_ata = + token::initialize_associated_token_account(trident, payer, *pubkey, authority); + + conditional_token_accounts.push(AccountMeta::new(conditional_token_user_ata, false)); + } + + let split_ix = conditional_vault::SplitTokensInstruction::data( + conditional_vault::SplitTokensInstructionData::new(amount), + ) + .accounts(conditional_vault::SplitTokensInstructionAccounts::new( + question, + vault, + conditional_vault_data.underlyingTokenAccount, + authority, + user_underlying_token_account, + TOKEN_PROGRAM_ID, + event_authority, + conditional_vault::program_id(), + )) + .remaining_accounts(conditional_token_accounts) + .instruction(); + + let res = trident.process_transaction(&[split_ix], message); + + assert!(res.is_success()); +} + +pub fn merge_tokens( + trident: &mut Trident, + question: Pubkey, + vault: Pubkey, + amount: u64, + authority: Pubkey, + message: Option<&str>, +) { + let conditional_vault_data = trident + .get_account_with_type::(&vault, None) + .expect("Conditional vault not found"); + + let user_underlying_token_account = trident.get_associated_token_address( + &conditional_vault_data.underlyingTokenMint, + &authority, + &TOKEN_PROGRAM_ID, + ); + + let event_authority = pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let mut conditional_token_accounts = vec![]; + + for pubkey in &conditional_vault_data.conditionalTokenMints { + conditional_token_accounts.push(AccountMeta::new(*pubkey, false)); + } + + for pubkey in &conditional_vault_data.conditionalTokenMints { + let conditional_token_user_ata = + token::initialize_associated_token_account(trident, authority, *pubkey, authority); + + conditional_token_accounts.push(AccountMeta::new(conditional_token_user_ata, false)); + } + + let merge_ix = conditional_vault::MergeTokensInstruction::data( + conditional_vault::MergeTokensInstructionData::new(amount), + ) + .accounts(conditional_vault::MergeTokensInstructionAccounts::new( + question, + vault, + conditional_vault_data.underlyingTokenAccount, + authority, + user_underlying_token_account, + TOKEN_PROGRAM_ID, + event_authority, + conditional_vault::program_id(), + )) + .remaining_accounts(conditional_token_accounts) + .instruction(); + + let res = trident.process_transaction(&[merge_ix], message); + + assert!(res.is_success()); +} diff --git a/trident-tests/common/constants.rs b/trident-tests/common/constants.rs new file mode 100644 index 000000000..34a7d07a6 --- /dev/null +++ b/trident-tests/common/constants.rs @@ -0,0 +1,69 @@ +use trident_fuzz::fuzzing::*; + +// ============================================================================ +// Addresses +pub const SQUADS_PROGRAM_ID: Pubkey = pubkey!("SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf"); +pub const SQUADS_PROGRAM_CONFIG_ID: Pubkey = + pubkey!("BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr"); +pub const SQUADS_PROGRAM_CONFIG_TREASURY_ID: Pubkey = + pubkey!("5DH2e3cJmFpyi6mk65EGFediunm4ui6BiKNUNrhWtD1b"); +pub const TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +pub const _TOKEN_2022_PROGRAM_ID: Pubkey = pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); +pub const ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = + pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); +pub const SOLANA_PROGRAM_ID: Pubkey = pubkey!("11111111111111111111111111111111"); +pub const USDC_MINT: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); +pub const MPL_TOKEN_METADATA_PROGRAM_ID: Pubkey = + pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"); +pub const RENT_SYSVAR_ID: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111"); +pub const DAMM_V2_PROGRAM_ID: Pubkey = pubkey!("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"); +pub const METEORA_CONFIG_ID: Pubkey = pubkey!("FaA6RM9enPh1tU9Y8LiGCq715JubLc49WGcYTdNvDfsc"); +pub const FEE_RECIPIENT_ID: Pubkey = pubkey!("6awyHMshBGVjJ3ozdSJdyyDE1CTAXUwrpNMaRGMsb4sf"); +pub const TOKEN_2022_PROGRAM_ID: Pubkey = pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); +// ============================================================================ + +// ============================================================================ +// Seeds +pub const SQUADS_SEED_PREFIX: &[u8] = b"multisig"; +pub const SQUADS_SEED_MULTISIG: &[u8] = b"multisig"; +pub const SQUADS_SEED_VAULT: &[u8] = b"vault"; +pub const SQUADS_SEED_SPENDING_LIMIT: &[u8] = b"spending_limit"; +pub const DAO_SEED_PREFIX: &[u8] = b"dao"; +pub const PROPOSAL_SEED_PREFIX: &[u8] = b"proposal"; +pub const CONDITIONAL_VAULT_SEED_PREFIX: &[u8] = b"conditional_vault"; +pub const QUESTION_SEED_PREFIX: &[u8] = b"question"; +pub const CONDITIONAL_TOKEN_SEED_PREFIX: &[u8] = b"conditional_token"; +pub const STAKE_SEED_PREFIX: &[u8] = b"stake"; +pub const AMM_POSITION_SEED_PREFIX: &[u8] = b"amm_position"; +pub const PERFORMANCE_PACKAGE_SEED_PREFIX: &[u8] = b"performance_package"; +pub const CHANGE_REQUEST_SEED_PREFIX: &[u8] = b"change_request"; +pub const SQUADS_SEED_PROGRAM_CONFIG: &[u8] = b"program_config"; +pub const POOL_CREATOR_AUTHORITY_SEED: &[u8] = b"damm_pool_creator_authority"; +pub const POOL_AUTHORITY_SEED: &[u8] = b"pool_authority"; +pub const POOL_PREFIX: &[u8] = b"pool"; +pub const POSITION_NFT_ACCOUNT_PREFIX: &[u8] = b"position_nft_account"; +pub const POSITION_PREFIX: &[u8] = b"position"; +pub const POSITION_NFT_MINT_PREFIX: &[u8] = b"position_nft_mint"; +pub const TOKEN_VAULT_PREFIX: &[u8] = b"token_vault"; +pub const BID_WALL_PREFIX: &[u8] = b"bid_wall"; +pub const LAUNCHPAD_SEED_PREFIX: &[u8] = b"launch"; +pub const TOKEN_METADATA_SEED_PREFIX: &[u8] = b"metadata"; +pub const LAUNCH_SIGNER_SEED_PREFIX: &[u8] = b"launch_signer"; +pub const FUNDING_RECORD_SEED_PREFIX: &[u8] = b"funding_record"; +// ============================================================================ + +// ============================================================================ +// Constants +pub const EVENT_AUTHORITY_SEED: &[u8] = b"__event_authority"; +// ============================================================================ + +// ============================================================================ +// Squads Multisig + +pub fn permissionless_account() -> Keypair { + Keypair::new_from_array([ + 249, 158, 188, 171, 243, 143, 1, 48, 87, 243, 209, 153, 144, 106, 23, 88, 161, 209, 65, + 217, 199, 121, 0, 250, 3, 203, 133, 138, 141, 112, 243, 38, + ]) +} +// ============================================================================ diff --git a/trident-tests/common/futarchy.rs b/trident-tests/common/futarchy.rs new file mode 100644 index 000000000..ed89f1630 --- /dev/null +++ b/trident-tests/common/futarchy.rs @@ -0,0 +1,650 @@ +use super::types::futarchy::SpotSwapInstructionAccounts; +use super::types::futarchy::SpotSwapInstructionData; +use super::types::futarchy::StakeToProposalInstructionAccounts; +use super::types::futarchy::StakeToProposalInstructionData; +use super::types::*; + +use trident_fuzz::fuzzing::*; +use trident_fuzz::trident::transaction_result::TransactionResult; + +use super::constants::*; +use super::types::futarchy::InitializeDaoParams; + +use super::pda; +use super::token; + +/// Standalone futarchy helper functions that work with Trident directly +pub fn initialize_dao( + trident: &mut Trident, + payer: Pubkey, + dao_creator: Pubkey, + base_mint: Pubkey, + quote_mint: Pubkey, + init_dao_params: InitializeDaoParams, + message: Option<&str>, +) -> (Pubkey, Pubkey) { + let dao = pda::get_dao_pda(trident, dao_creator, init_dao_params.nonce); + let squads_multisig = pda::get_squads_multisig_pda(trident, dao); + + let squads_multisig_vault = pda::get_squads_multisig_vault_pda(trident, squads_multisig); + + let spending_limit = pda::get_squads_multisig_spending_limit_pda(trident, squads_multisig, dao); + + let futarchy_amm_base_vault = + trident.get_associated_token_address(&base_mint, &dao, &TOKEN_PROGRAM_ID); + + let futarchy_amm_quote_vault = + trident.get_associated_token_address("e_mint, &dao, &TOKEN_PROGRAM_ID); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let init_dao = futarchy::InitializeDaoInstruction::data( + futarchy::InitializeDaoInstructionData::new(init_dao_params), + ) + .accounts(futarchy::InitializeDaoInstructionAccounts::new( + dao, + dao_creator, + payer, + solana_sdk::system_program::ID, + base_mint, + quote_mint, + squads_multisig, + squads_multisig_vault, + SQUADS_PROGRAM_ID, + SQUADS_PROGRAM_CONFIG_ID, + SQUADS_PROGRAM_CONFIG_TREASURY_ID, + spending_limit, + futarchy_amm_base_vault, + futarchy_amm_quote_vault, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + let res = trident.process_transaction(&[init_dao], message); + + assert!(res.is_success()); + + (dao, squads_multisig) +} + +#[allow(clippy::too_many_arguments)] +pub fn initialize_proposal( + trident: &mut Trident, + dao: Pubkey, + squads_proposal: Pubkey, + squads_multisig: Pubkey, + proposer: Pubkey, + payer: Pubkey, + question: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + message: Option<&str>, +) -> Pubkey { + let proposal = pda::get_proposal_pda(trident, squads_proposal); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let init_prop = futarchy::InitializeProposalInstruction::data( + futarchy::InitializeProposalInstructionData::new(), + ) + .accounts(futarchy::InitializeProposalInstructionAccounts::new( + proposal, + squads_proposal, + squads_multisig, + dao, + question, + quote_vault, + base_vault, + proposer, + payer, + solana_sdk::system_program::ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + let res = trident.process_transaction(&[init_prop], message); + + assert!(res.is_success()); + + proposal +} + +#[allow(clippy::too_many_arguments)] +pub fn stake_to_proposal( + trident: &mut Trident, + dao: Pubkey, + payer: Pubkey, + proposal: Pubkey, + staker: Pubkey, + proposal_base_account: Pubkey, + params: futarchy::StakeToProposalParams, + message: Option<&str>, +) { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let stake_account = pda::get_stake_account_pda(trident, proposal, staker); + + let staker_base_ata = + trident.get_associated_token_address(&dao_data.baseMint, &staker, &TOKEN_PROGRAM_ID); + + let stake_to_proposal_ix = + futarchy::StakeToProposalInstruction::data(StakeToProposalInstructionData::new(params)) + .accounts(StakeToProposalInstructionAccounts::new( + proposal, + dao, + staker_base_ata, + proposal_base_account, + stake_account, + staker, + payer, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + solana_sdk::system_program::ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + let res = trident.process_transaction(&[stake_to_proposal_ix], message); + + assert!(res.is_success()); +} + +#[allow(clippy::too_many_arguments)] +pub fn launch_proposal( + trident: &mut Trident, + squads_multisig: Pubkey, + squads_proposal: Pubkey, + dao: Pubkey, + payer: Pubkey, + proposal: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + message: Option<&str>, +) { + let base_vault_data = trident + .get_account_with_type::(&base_vault, None) + .expect("Base conditional vault not found"); + + let quote_vault_data = trident + .get_account_with_type::("e_vault, None) + .expect("Quote conditional vault not found"); + + let pass_base_mint = base_vault_data.conditionalTokenMints[1]; + let pass_quote_mint = quote_vault_data.conditionalTokenMints[1]; + let fail_base_mint = base_vault_data.conditionalTokenMints[0]; + let fail_quote_mint = quote_vault_data.conditionalTokenMints[0]; + + let amm_pass_base_vault = + trident.get_associated_token_address(&pass_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_pass_quote_vault = + trident.get_associated_token_address(&pass_quote_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_base_vault = + trident.get_associated_token_address(&fail_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_quote_vault = + trident.get_associated_token_address(&fail_quote_mint, &dao, &TOKEN_PROGRAM_ID); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let launch_ix = + futarchy::LaunchProposalInstruction::data(futarchy::LaunchProposalInstructionData::new()) + .accounts(futarchy::LaunchProposalInstructionAccounts::new( + proposal, + base_vault, + quote_vault, + pass_base_mint, + pass_quote_mint, + fail_base_mint, + fail_quote_mint, + dao, + payer, + amm_pass_base_vault, + amm_pass_quote_vault, + amm_fail_base_vault, + amm_fail_quote_vault, + squads_multisig, + squads_proposal, + solana_sdk::system_program::ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + let res = trident.process_transaction(&[launch_ix], message); + + assert!(res.is_success()); +} + +pub fn spot_swap( + trident: &mut Trident, + dao: Pubkey, + user: Pubkey, + params: futarchy::SpotSwapParams, + message: Option<&str>, +) -> TransactionResult { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + + let user_base_account = + trident.get_associated_token_address(&dao_data.baseMint, &user, &TOKEN_PROGRAM_ID); + + let user_quote_account = + trident.get_associated_token_address(&dao_data.quoteMint, &user, &TOKEN_PROGRAM_ID); + + let amm_base_vault = + trident.get_associated_token_address(&dao_data.baseMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_quote_vault = + trident.get_associated_token_address(&dao_data.quoteMint, &dao, &TOKEN_PROGRAM_ID); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + let spot_swap_ix = futarchy::SpotSwapInstruction::data(SpotSwapInstructionData::new(params)) + .accounts(SpotSwapInstructionAccounts::new( + dao, + user_base_account, + user_quote_account, + amm_base_vault, + amm_quote_vault, + user, + TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + trident.process_transaction(&[spot_swap_ix], message) +} + +pub fn add_liqidity( + trident: &mut Trident, + dao: Pubkey, + payer: Pubkey, + liquidity_provider: Pubkey, + params: futarchy::ProvideLiquidityParams, + message: Option<&str>, +) -> TransactionResult { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + + let liquidity_provider_base_account = trident.get_associated_token_address( + &dao_data.baseMint, + &liquidity_provider, + &TOKEN_PROGRAM_ID, + ); + let liquidity_provider_quote_account = trident.get_associated_token_address( + &dao_data.quoteMint, + &liquidity_provider, + &TOKEN_PROGRAM_ID, + ); + + let amm_base_vault = + trident.get_associated_token_address(&dao_data.baseMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_quote_vault = + trident.get_associated_token_address(&dao_data.quoteMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_position = pda::get_amm_position_pda(trident, dao, liquidity_provider); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let add_liq = futarchy::ProvideLiquidityInstruction::data( + futarchy::ProvideLiquidityInstructionData::new(params), + ) + .accounts(futarchy::ProvideLiquidityInstructionAccounts::new( + dao, + liquidity_provider, + liquidity_provider_base_account, + liquidity_provider_quote_account, + payer, + solana_sdk::system_program::ID, + amm_base_vault, + amm_quote_vault, + amm_position, + TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + trident.process_transaction(&[add_liq], message) +} + +pub fn withdraw_liquidity( + trident: &mut Trident, + dao: Pubkey, + payer: Pubkey, + liquidity_provider: Pubkey, + params: futarchy::WithdrawLiquidityParams, + message: Option<&str>, +) -> TransactionResult { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + + let liquidity_provider_base_account = token::initialize_associated_token_account( + trident, + payer, + dao_data.baseMint, + liquidity_provider, + ); + + let liquidity_provider_quote_account = token::initialize_associated_token_account( + trident, + payer, + dao_data.quoteMint, + liquidity_provider, + ); + + let amm_base_vault = + trident.get_associated_token_address(&dao_data.baseMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_quote_vault = + trident.get_associated_token_address(&dao_data.quoteMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_position = pda::get_amm_position_pda(trident, dao, liquidity_provider); + + let amm_position_data = trident + .get_account_with_type::(&amm_position, None) + .expect("Amm position not found"); + + let position_authority = amm_position_data.positionAuthority; + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let withdraw_ix = futarchy::WithdrawLiquidityInstruction::data( + futarchy::WithdrawLiquidityInstructionData::new(params), + ) + .accounts(futarchy::WithdrawLiquidityInstructionAccounts::new( + dao, + position_authority, + liquidity_provider_base_account, + liquidity_provider_quote_account, + amm_base_vault, + amm_quote_vault, + amm_position, + TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + trident.process_transaction(&[withdraw_ix], message) +} + +#[allow(clippy::too_many_arguments)] +pub fn conditional_swap( + trident: &mut Trident, + dao: Pubkey, + payer: Pubkey, + proposal: Pubkey, + trader: Pubkey, + question: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + params: futarchy::ConditionalSwapParams, + message: Option<&str>, +) -> TransactionResult { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + + let base_vault_data = trident + .get_account_with_type::(&base_vault, None) + .expect("Base conditional vault not found"); + + let quote_vault_data = trident + .get_account_with_type::("e_vault, None) + .expect("Quote conditional vault not found"); + + let amm_base_vault = + trident.get_associated_token_address(&dao_data.baseMint, &dao, &TOKEN_PROGRAM_ID); + + let amm_quote_vault = + trident.get_associated_token_address(&dao_data.quoteMint, &dao, &TOKEN_PROGRAM_ID); + + let pass_base_mint = base_vault_data.conditionalTokenMints[1]; + let pass_quote_mint = quote_vault_data.conditionalTokenMints[1]; + let fail_base_mint = base_vault_data.conditionalTokenMints[0]; + let fail_quote_mint = quote_vault_data.conditionalTokenMints[0]; + + let amm_pass_base_vault = + trident.get_associated_token_address(&pass_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_pass_quote_vault = + trident.get_associated_token_address(&pass_quote_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_base_vault = + trident.get_associated_token_address(&fail_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_quote_vault = + trident.get_associated_token_address(&fail_quote_mint, &dao, &TOKEN_PROGRAM_ID); + + let (user_input_account, user_output_account) = evaluate_conditional_swap_token_accounts( + trident, + payer, + trader, + fail_base_mint, + pass_base_mint, + fail_quote_mint, + pass_quote_mint, + ¶ms, + ); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + + let conditional_vault_event_authority = + pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let conditional_swap = futarchy::ConditionalSwapInstruction::data( + futarchy::ConditionalSwapInstructionData::new(params), + ) + .accounts(futarchy::ConditionalSwapInstructionAccounts::new( + dao, + amm_base_vault, + amm_quote_vault, + proposal, + amm_pass_base_vault, + amm_pass_quote_vault, + amm_fail_base_vault, + amm_fail_quote_vault, + trader, + user_input_account, + user_output_account, + base_vault, + base_vault_data.underlyingTokenAccount, + quote_vault, + quote_vault_data.underlyingTokenAccount, + pass_base_mint, + fail_base_mint, + pass_quote_mint, + fail_quote_mint, + conditional_vault::program_id(), + conditional_vault_event_authority, + question, + TOKEN_PROGRAM_ID, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + trident.process_transaction(&[conditional_swap], message) +} + +#[allow(clippy::too_many_arguments)] +pub fn finalize_proposal( + trident: &mut Trident, + dao: Pubkey, + proposal: Pubkey, + question: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + message: Option<&str>, +) -> TransactionResult { + let dao_data = trident + .get_account_with_type::(&dao, None) + .expect("Dao not found"); + let proposal_data = trident + .get_account_with_type::(&proposal, None) + .expect("Proposal not found"); + let base_vault_data = trident + .get_account_with_type::(&base_vault, None) + .expect("Base conditional vault not found"); + let quote_vault_data = trident + .get_account_with_type::("e_vault, None) + .expect("Quote conditional vault not found"); + + let amm_base_vault = + trident.get_associated_token_address(&dao_data.baseMint, &dao, &TOKEN_PROGRAM_ID); + let amm_quote_vault = + trident.get_associated_token_address(&dao_data.quoteMint, &dao, &TOKEN_PROGRAM_ID); + + let pass_base_mint = proposal_data.passBaseMint; + let pass_quote_mint = proposal_data.passQuoteMint; + let fail_base_mint = proposal_data.failBaseMint; + let fail_quote_mint = proposal_data.failQuoteMint; + + let amm_pass_base_vault = + trident.get_associated_token_address(&pass_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_pass_quote_vault = + trident.get_associated_token_address(&pass_quote_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_base_vault = + trident.get_associated_token_address(&fail_base_mint, &dao, &TOKEN_PROGRAM_ID); + let amm_fail_quote_vault = + trident.get_associated_token_address(&fail_quote_mint, &dao, &TOKEN_PROGRAM_ID); + + let event_authority = pda::get_event_authority_pda(trident, futarchy::program_id()); + let vault_event_authority = + pda::get_event_authority_pda(trident, conditional_vault::program_id()); + + let finalize_ix = futarchy::FinalizeProposalInstruction::data( + futarchy::FinalizeProposalInstructionData::new(), + ) + .accounts(futarchy::FinalizeProposalInstructionAccounts::new( + proposal, + dao, + question, + proposal_data.squadsProposal, + dao_data.squadsMultisig, + SQUADS_PROGRAM_ID, + amm_pass_base_vault, + amm_pass_quote_vault, + amm_fail_base_vault, + amm_fail_quote_vault, + amm_base_vault, + amm_quote_vault, + conditional_vault::program_id(), + vault_event_authority, + TOKEN_PROGRAM_ID, + quote_vault, + quote_vault_data.underlyingTokenAccount, + pass_quote_mint, + fail_quote_mint, + pass_base_mint, + fail_base_mint, + base_vault, + base_vault_data.underlyingTokenAccount, + event_authority, + futarchy::program_id(), + )) + .instruction(); + + trident.process_transaction(&[finalize_ix], message) +} + +#[allow(clippy::too_many_arguments)] +fn evaluate_conditional_swap_token_accounts( + trident: &mut Trident, + payer: Pubkey, + user: Pubkey, + base_fail_mint: Pubkey, + base_pass_mint: Pubkey, + quote_fail_mint: Pubkey, + quote_pass_mint: Pubkey, + params: &futarchy::ConditionalSwapParams, +) -> (Pubkey, Pubkey) { + match (¶ms.swapType, ¶ms.market) { + (futarchy::SwapType::Buy, futarchy::Market::Pass) => { + let user_in = + trident.get_associated_token_address("e_pass_mint, &user, &TOKEN_PROGRAM_ID); + let user_out = + trident.get_associated_token_address(&base_pass_mint, &user, &TOKEN_PROGRAM_ID); + + match trident.get_token_account(user_out) { + Ok(_) => (user_in, user_out), + Err(_) => { + let init_ata = token::initialize_associated_token_account( + trident, + payer, + base_pass_mint, + user, + ); + (user_in, init_ata) + } + } + } + (futarchy::SwapType::Buy, futarchy::Market::Fail) => { + let user_in = + trident.get_associated_token_address("e_fail_mint, &user, &TOKEN_PROGRAM_ID); + let user_out = + trident.get_associated_token_address(&base_fail_mint, &user, &TOKEN_PROGRAM_ID); + match trident.get_token_account(user_out) { + Ok(_) => (user_in, user_out), + Err(_) => { + let init_ata = token::initialize_associated_token_account( + trident, + payer, + base_fail_mint, + user, + ); + (user_in, init_ata) + } + } + } + (futarchy::SwapType::Sell, futarchy::Market::Pass) => { + let user_in = + trident.get_associated_token_address(&base_pass_mint, &user, &TOKEN_PROGRAM_ID); + let user_out = + trident.get_associated_token_address("e_pass_mint, &user, &TOKEN_PROGRAM_ID); + match trident.get_token_account(user_out) { + Ok(_) => (user_in, user_out), + Err(_) => { + let init_ata = token::initialize_associated_token_account( + trident, + payer, + quote_pass_mint, + user, + ); + (user_in, init_ata) + } + } + } + (futarchy::SwapType::Sell, futarchy::Market::Fail) => { + let user_in = + trident.get_associated_token_address(&base_fail_mint, &user, &TOKEN_PROGRAM_ID); + let user_out = + trident.get_associated_token_address("e_fail_mint, &user, &TOKEN_PROGRAM_ID); + match trident.get_token_account(user_out) { + Ok(_) => (user_in, user_out), + Err(_) => { + let init_ata = token::initialize_associated_token_account( + trident, + payer, + quote_fail_mint, + user, + ); + (user_in, init_ata) + } + } + } + (_, _) => panic!("Conditional Swap does not support Spot swaps"), + } +} diff --git a/trident-tests/common/mod.rs b/trident-tests/common/mod.rs new file mode 100644 index 000000000..56cca548e --- /dev/null +++ b/trident-tests/common/mod.rs @@ -0,0 +1,7 @@ +pub mod conditional_vault; +pub mod constants; +pub mod futarchy; +pub mod pda; +pub mod squads; +pub mod token; +pub mod types; diff --git a/trident-tests/common/pda.rs b/trident-tests/common/pda.rs new file mode 100644 index 000000000..1cb78f60c --- /dev/null +++ b/trident-tests/common/pda.rs @@ -0,0 +1,321 @@ +use super::constants::*; +use super::types::bid_wall; +use super::types::conditional_vault; +use super::types::futarchy; +use super::types::launchpad_v_7; +use super::types::price_based_performance_package; + +use trident_fuzz::fuzzing::*; + +/// Standalone PDA helper functions that work with Trident directly +pub fn get_proposal_pda(trident: &mut Trident, squads_proposal: Pubkey) -> Pubkey { + trident + .find_program_address( + &[PROPOSAL_SEED_PREFIX, squads_proposal.as_ref()], + &futarchy::program_id(), + ) + .0 +} + +pub fn get_performance_package_pda(trident: &mut Trident, create_key: Pubkey) -> Pubkey { + trident + .find_program_address( + &[PERFORMANCE_PACKAGE_SEED_PREFIX, create_key.as_ref()], + &price_based_performance_package::program_id(), + ) + .0 +} + +pub fn get_change_request_pda( + trident: &mut Trident, + performance_package: Pubkey, + proposer: Pubkey, + pda_nonce: u32, +) -> Pubkey { + trident + .find_program_address( + &[ + CHANGE_REQUEST_SEED_PREFIX, + performance_package.as_ref(), + proposer.as_ref(), + pda_nonce.to_le_bytes().as_ref(), + ], + &price_based_performance_package::program_id(), + ) + .0 +} + +pub fn get_launchpad_pda(trident: &mut Trident, base_mint: Pubkey) -> Pubkey { + trident + .find_program_address( + &[LAUNCHPAD_SEED_PREFIX, base_mint.as_ref()], + &launchpad_v_7::program_id(), + ) + .0 +} +pub fn get_token_metadata_pda(trident: &mut Trident, base_mint: Pubkey) -> Pubkey { + trident + .find_program_address( + &[ + TOKEN_METADATA_SEED_PREFIX, + MPL_TOKEN_METADATA_PROGRAM_ID.as_ref(), + base_mint.as_ref(), + ], + &MPL_TOKEN_METADATA_PROGRAM_ID, + ) + .0 +} +pub fn get_launch_signer_pda(trident: &mut Trident, launch: Pubkey) -> Pubkey { + trident + .find_program_address( + &[LAUNCH_SIGNER_SEED_PREFIX, launch.as_ref()], + &launchpad_v_7::program_id(), + ) + .0 +} + +pub fn get_funding_record_pda(trident: &mut Trident, launch: Pubkey, funder: Pubkey) -> Pubkey { + trident + .find_program_address( + &[FUNDING_RECORD_SEED_PREFIX, launch.as_ref(), funder.as_ref()], + &launchpad_v_7::program_id(), + ) + .0 +} +pub fn get_squads_program_config_pda(trident: &mut Trident) -> Pubkey { + trident + .find_program_address( + &[SQUADS_SEED_PREFIX, SQUADS_SEED_PROGRAM_CONFIG], + &SQUADS_PROGRAM_ID, + ) + .0 +} +pub fn get_pool_creator_authority_pda(trident: &mut Trident) -> Pubkey { + trident + .find_program_address(&[POOL_CREATOR_AUTHORITY_SEED], &launchpad_v_7::program_id()) + .0 +} +pub fn get_pool_authority_pda(trident: &mut Trident) -> Pubkey { + trident + .find_program_address(&[POOL_AUTHORITY_SEED], &DAMM_V2_PROGRAM_ID) + .0 +} +fn max_key(left: &Pubkey, right: &Pubkey) -> [u8; 32] { + std::cmp::max(left, right).to_bytes() +} + +fn min_key(left: &Pubkey, right: &Pubkey) -> [u8; 32] { + std::cmp::min(left, right).to_bytes() +} +pub fn get_pool_pda( + trident: &mut Trident, + config: Pubkey, + base_mint: Pubkey, + quote_mint: Pubkey, +) -> Pubkey { + let max_key = max_key(&base_mint, "e_mint); + let min_key = min_key(&base_mint, "e_mint); + trident + .find_program_address( + &[ + POOL_PREFIX, + config.as_ref(), + max_key.as_ref(), + min_key.as_ref(), + ], + &DAMM_V2_PROGRAM_ID, + ) + .0 +} +pub fn get_position_nft_account_pda(trident: &mut Trident, position_nft_mint: Pubkey) -> Pubkey { + trident + .find_program_address( + &[POSITION_NFT_ACCOUNT_PREFIX, position_nft_mint.as_ref()], + &DAMM_V2_PROGRAM_ID, + ) + .0 +} +pub fn get_position_pda(trident: &mut Trident, position_nft_mint: Pubkey) -> Pubkey { + trident + .find_program_address( + &[POSITION_PREFIX, position_nft_mint.as_ref()], + &DAMM_V2_PROGRAM_ID, + ) + .0 +} +pub fn get_position_nft_mint_pda(trident: &mut Trident, base_mint: Pubkey) -> Pubkey { + trident + .find_program_address( + &[POSITION_NFT_MINT_PREFIX, base_mint.as_ref()], + &launchpad_v_7::program_id(), + ) + .0 +} +pub fn get_token_a_vault_pda(trident: &mut Trident, base_mint: Pubkey, pool: Pubkey) -> Pubkey { + trident + .find_program_address( + &[TOKEN_VAULT_PREFIX, base_mint.as_ref(), pool.as_ref()], + &DAMM_V2_PROGRAM_ID, + ) + .0 +} +pub fn get_token_b_vault_pda(trident: &mut Trident, quote_mint: Pubkey, pool: Pubkey) -> Pubkey { + trident + .find_program_address( + &[TOKEN_VAULT_PREFIX, quote_mint.as_ref(), pool.as_ref()], + &DAMM_V2_PROGRAM_ID, + ) + .0 +} +pub fn get_bid_wall_pda(trident: &mut Trident, base_mint: Pubkey, launch_signer: Pubkey) -> Pubkey { + trident + .find_program_address( + &[ + BID_WALL_PREFIX, + base_mint.as_ref(), + launch_signer.as_ref(), + 0_u64.to_le_bytes().as_ref(), + ], + &bid_wall::program_id(), + ) + .0 +} + +pub fn get_conditional_vault_pda( + trident: &mut Trident, + question: Pubkey, + underlying_token_mint: Pubkey, +) -> Pubkey { + trident + .find_program_address( + &[ + CONDITIONAL_VAULT_SEED_PREFIX, + question.as_ref(), + underlying_token_mint.as_ref(), + ], + &conditional_vault::program_id(), + ) + .0 +} + +pub fn get_event_authority_pda(trident: &mut Trident, program_id: Pubkey) -> Pubkey { + trident + .find_program_address(&[EVENT_AUTHORITY_SEED], &program_id) + .0 +} + +pub fn get_question_pda( + trident: &mut Trident, + question_id: [u8; 32], + oracle: Pubkey, + num_outcomes: u8, +) -> Pubkey { + trident + .find_program_address( + &[ + QUESTION_SEED_PREFIX, + question_id.as_ref(), + oracle.as_ref(), + &[num_outcomes], + ], + &conditional_vault::program_id(), + ) + .0 +} + +pub fn get_dao_pda(trident: &mut Trident, dao_creator: Pubkey, nonce: u64) -> Pubkey { + trident + .find_program_address( + &[ + DAO_SEED_PREFIX, + dao_creator.as_ref(), + nonce.to_le_bytes().as_ref(), + ], + &futarchy::program_id(), + ) + .0 +} + +pub fn get_squads_multisig_pda(trident: &mut Trident, dao: Pubkey) -> Pubkey { + trident + .find_program_address( + &[SQUADS_SEED_PREFIX, SQUADS_SEED_MULTISIG, dao.as_ref()], + &SQUADS_PROGRAM_ID, + ) + .0 +} + +pub fn get_squads_multisig_vault_pda(trident: &mut Trident, squads_multisig: Pubkey) -> Pubkey { + trident + .find_program_address( + &[ + SQUADS_SEED_PREFIX, + squads_multisig.as_ref(), + SQUADS_SEED_VAULT, + 0_u8.to_le_bytes().as_ref(), + ], + &SQUADS_PROGRAM_ID, + ) + .0 +} + +pub fn get_squads_multisig_spending_limit_pda( + trident: &mut Trident, + squads_multisig: Pubkey, + dao: Pubkey, +) -> Pubkey { + trident + .find_program_address( + &[ + SQUADS_SEED_PREFIX, + squads_multisig.as_ref(), + SQUADS_SEED_SPENDING_LIMIT, + dao.as_ref(), + ], + &SQUADS_PROGRAM_ID, + ) + .0 +} + +pub fn get_conditional_token_mint_pda( + trident: &mut Trident, + conditional_vault: Pubkey, + index: u8, +) -> Pubkey { + trident + .find_program_address( + &[ + CONDITIONAL_TOKEN_SEED_PREFIX, + conditional_vault.as_ref(), + &[index], + ], + &conditional_vault::program_id(), + ) + .0 +} + +pub fn get_stake_account_pda(trident: &mut Trident, proposal: Pubkey, staker: Pubkey) -> Pubkey { + trident + .find_program_address( + &[STAKE_SEED_PREFIX, proposal.as_ref(), staker.as_ref()], + &futarchy::program_id(), + ) + .0 +} + +pub fn get_amm_position_pda( + trident: &mut Trident, + dao: Pubkey, + position_authority: Pubkey, +) -> Pubkey { + trident + .find_program_address( + &[ + AMM_POSITION_SEED_PREFIX, + dao.as_ref(), + position_authority.as_ref(), + ], + &futarchy::program_id(), + ) + .0 +} diff --git a/trident-tests/common/squads.rs b/trident-tests/common/squads.rs new file mode 100644 index 000000000..b13fab6bd --- /dev/null +++ b/trident-tests/common/squads.rs @@ -0,0 +1,72 @@ +use squads_multisig::client::ProposalCreateAccounts; +use squads_multisig::client::ProposalCreateArgs; +use squads_multisig::client::VaultTransactionCreateAccounts; +use squads_multisig::state::TransactionMessage; +use trident_fuzz::fuzzing::*; + +/// Standalone Squads helper functions that work with Trident directly +#[allow(clippy::too_many_arguments)] +pub fn initialize_vault_transaction( + trident: &mut Trident, + multisig: Pubkey, + squads_tx_creator: Pubkey, + payer: Pubkey, + vault_index: u8, + num_ephemeral_signers: u8, + transaction_message: TransactionMessage, + message: Option<&str>, +) { + let transaction = squads_multisig::pda::get_transaction_pda(&multisig, 1, None).0; + let vault_tx_create = squads_multisig::client::vault_transaction_create( + VaultTransactionCreateAccounts { + multisig, + transaction, + creator: squads_tx_creator, + rent_payer: payer, + system_program: solana_sdk::system_program::ID, + }, + vault_index, + num_ephemeral_signers, + &transaction_message, + None, + None, + ); + + let res = trident.process_transaction(&[vault_tx_create], message); + + assert!(res.is_success()); +} + +pub fn initialize_squads_proposal( + trident: &mut Trident, + multisig: Pubkey, + squads_proposal_creator: Pubkey, + payer: Pubkey, + proposal_create_args: ProposalCreateArgs, + message: Option<&str>, +) -> Pubkey { + let squads_proposal = squads_multisig::pda::get_proposal_pda( + &multisig, + proposal_create_args.transaction_index, + None, + ) + .0; + + let create_proposal = squads_multisig::client::proposal_create( + ProposalCreateAccounts { + multisig, + proposal: squads_proposal, + creator: squads_proposal_creator, + rent_payer: payer, + system_program: solana_sdk::system_program::ID, + }, + proposal_create_args, + None, + ); + + let res = trident.process_transaction(&[create_proposal], message); + + assert!(res.is_success()); + + squads_proposal +} diff --git a/trident-tests/common/token.rs b/trident-tests/common/token.rs new file mode 100644 index 000000000..5ff51ccdf --- /dev/null +++ b/trident-tests/common/token.rs @@ -0,0 +1,77 @@ +use trident_fuzz::fuzzing::*; + +use super::constants::*; + +/// Standalone token helper functions that work with Trident directly +pub fn initialize_mint( + trident: &mut Trident, + payer: Pubkey, + mint: Pubkey, + decimals: u8, + owner: Pubkey, + freeze_authority: Option<&Pubkey>, + message: Option<&str>, +) { + let ix = trident.initialize_mint(&payer, &mint, decimals, &owner, freeze_authority); + let res = trident.process_transaction(&ix, message); + assert!(res.is_success()); +} + +pub fn initialize_associated_token_account( + trident: &mut Trident, + payer: Pubkey, + mint: Pubkey, + owner: Pubkey, +) -> Pubkey { + let ata = trident.get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + match trident.get_token_account(ata) { + Ok(_) => { + // account already exists -- skip + } + Err(_) => { + let ix = trident.initialize_associated_token_account(&payer, &mint, &owner); + let res = trident.process_transaction(&[ix], None); + + assert!(res.is_success()); + } + } + + ata +} + +pub fn mint_to( + trident: &mut Trident, + token_account_address: Pubkey, + mint_address: Pubkey, + mint_authority: Pubkey, + amount: u64, +) { + let mint = trident.mint_to( + &token_account_address, + &mint_address, + &mint_authority, + amount, + ); + + let res = trident.process_transaction(&[mint], None); + + assert!(res.is_success()); +} + +pub fn get_or_initialize_associated_token_account( + trident: &mut Trident, + payer: Pubkey, + mint: Pubkey, + owner: Pubkey, +) -> Pubkey { + let ata = trident.get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + if trident.get_token_account(ata).is_err() { + let ix = trident.initialize_associated_token_account(&payer, &mint, &owner); + let res = trident.process_transaction(&[ix], None); + assert!(res.is_success()); + } + + ata +} diff --git a/trident-tests/common/types.rs b/trident-tests/common/types.rs new file mode 100644 index 000000000..f1612f3ae --- /dev/null +++ b/trident-tests/common/types.rs @@ -0,0 +1,15057 @@ +#![allow(clippy::all)] +#![allow(unused)] +#![allow(non_snake_case)] +//! # Trident Generated Types +//! +//! This file is automatically generated by Trident. +//! **DO NOT EDIT THIS FILE MANUALLY** + +#![allow(dead_code)] +#![allow(unused_imports)] + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use trident_fuzz::fuzzing::*; +use trident_fuzz::AccountDiscriminator; + +// ============================================================================ +// PROGRAM MODULES +// ============================================================================ + +// ---------------------------------------------------------------------------- +// Program: conditional_vault +// ---------------------------------------------------------------------------- +pub mod conditional_vault { + use super::*; + + // ------------------------------------------------------------------------ + // Program ID + // ------------------------------------------------------------------------ + + /// Returns the program ID for conditional_vault + pub fn program_id() -> Pubkey { + pubkey!("VLTX1ishMBbcX3rdBWGssxawAo1Q2X2qxYFYqiGodVg") + } + + // ------------------------------------------------------------------------ + // Instructions + // ------------------------------------------------------------------------ + + // .................................................................... + // Instruction: InitializeQuestion + // .................................................................... + + /// Main instruction struct for InitializeQuestion + pub struct InitializeQuestionInstruction { + pub accounts: InitializeQuestionInstructionAccountMetas, + pub data: InitializeQuestionInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeQuestion instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeQuestionInstructionAccountMetas { + pub question: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeQuestion instruction + #[derive(Debug, Clone)] + pub struct InitializeQuestionInstructionAccounts { + pub question: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeQuestionInstructionAccounts { + pub fn new( + question: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + question, + + payer, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeQuestion + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeQuestionInstructionData { + pub args: InitializeQuestionArgs, + } + + impl InitializeQuestionInstructionData { + pub fn new(args: InitializeQuestionArgs) -> Self { + Self { args } + } + } + + /// Implementation for InitializeQuestionInstruction + impl InitializeQuestionInstruction { + fn discriminator() -> [u8; 8] { + [245u8, 151u8, 106u8, 188u8, 88u8, 44u8, 65u8, 212u8] + } + + pub fn data(data: InitializeQuestionInstructionData) -> Self { + Self { + accounts: InitializeQuestionInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeQuestionInstructionAccounts) -> Self { + self.accounts.question = AccountMeta::new(accounts.question, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ResolveQuestion + // .................................................................... + + /// Main instruction struct for ResolveQuestion + pub struct ResolveQuestionInstruction { + pub accounts: ResolveQuestionInstructionAccountMetas, + pub data: ResolveQuestionInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ResolveQuestion instruction + #[derive(Debug, Clone, Default)] + pub struct ResolveQuestionInstructionAccountMetas { + pub question: AccountMeta, + + pub oracle: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ResolveQuestion instruction + #[derive(Debug, Clone)] + pub struct ResolveQuestionInstructionAccounts { + pub question: Pubkey, + + pub oracle: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ResolveQuestionInstructionAccounts { + pub fn new( + question: Pubkey, + + oracle: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + question, + + oracle, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ResolveQuestion + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ResolveQuestionInstructionData { + pub args: ResolveQuestionArgs, + } + + impl ResolveQuestionInstructionData { + pub fn new(args: ResolveQuestionArgs) -> Self { + Self { args } + } + } + + /// Implementation for ResolveQuestionInstruction + impl ResolveQuestionInstruction { + fn discriminator() -> [u8; 8] { + [52u8, 32u8, 224u8, 179u8, 180u8, 8u8, 0u8, 246u8] + } + + pub fn data(data: ResolveQuestionInstructionData) -> Self { + Self { + accounts: ResolveQuestionInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ResolveQuestionInstructionAccounts) -> Self { + self.accounts.question = AccountMeta::new(accounts.question, false); + + self.accounts.oracle = AccountMeta::new_readonly(accounts.oracle, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.oracle.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: InitializeConditionalVault + // .................................................................... + + /// Main instruction struct for InitializeConditionalVault + pub struct InitializeConditionalVaultInstruction { + pub accounts: InitializeConditionalVaultInstructionAccountMetas, + pub data: InitializeConditionalVaultInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeConditionalVault instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeConditionalVaultInstructionAccountMetas { + pub vault: AccountMeta, + + pub question: AccountMeta, + + pub underlyingTokenMint: AccountMeta, + + pub vaultUnderlyingTokenAccount: AccountMeta, + + pub payer: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeConditionalVault instruction + #[derive(Debug, Clone)] + pub struct InitializeConditionalVaultInstructionAccounts { + pub vault: Pubkey, + + pub question: Pubkey, + + pub underlyingTokenMint: Pubkey, + + pub vaultUnderlyingTokenAccount: Pubkey, + + pub payer: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeConditionalVaultInstructionAccounts { + pub fn new( + vault: Pubkey, + + question: Pubkey, + + underlyingTokenMint: Pubkey, + + vaultUnderlyingTokenAccount: Pubkey, + + payer: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + vault, + + question, + + underlyingTokenMint, + + vaultUnderlyingTokenAccount, + + payer, + + tokenProgram, + + associatedTokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeConditionalVault + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeConditionalVaultInstructionData {} + + impl InitializeConditionalVaultInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for InitializeConditionalVaultInstruction + impl InitializeConditionalVaultInstruction { + fn discriminator() -> [u8; 8] { + [37u8, 88u8, 250u8, 212u8, 54u8, 218u8, 227u8, 175u8] + } + + pub fn data(data: InitializeConditionalVaultInstructionData) -> Self { + Self { + accounts: InitializeConditionalVaultInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeConditionalVaultInstructionAccounts) -> Self { + self.accounts.vault = AccountMeta::new(accounts.vault, false); + + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.underlyingTokenMint = + AccountMeta::new_readonly(accounts.underlyingTokenMint, false); + + self.accounts.vaultUnderlyingTokenAccount = + AccountMeta::new(accounts.vaultUnderlyingTokenAccount, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.vault.clone()); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.underlyingTokenMint.clone()); + + metas.push(self.accounts.vaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: SplitTokens + // .................................................................... + + /// Main instruction struct for SplitTokens + pub struct SplitTokensInstruction { + pub accounts: SplitTokensInstructionAccountMetas, + pub data: SplitTokensInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for SplitTokens instruction + #[derive(Debug, Clone, Default)] + pub struct SplitTokensInstructionAccountMetas { + pub question: AccountMeta, + + pub vault: AccountMeta, + + pub vaultUnderlyingTokenAccount: AccountMeta, + + pub authority: AccountMeta, + + pub userUnderlyingTokenAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for SplitTokens instruction + #[derive(Debug, Clone)] + pub struct SplitTokensInstructionAccounts { + pub question: Pubkey, + + pub vault: Pubkey, + + pub vaultUnderlyingTokenAccount: Pubkey, + + pub authority: Pubkey, + + pub userUnderlyingTokenAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl SplitTokensInstructionAccounts { + pub fn new( + question: Pubkey, + + vault: Pubkey, + + vaultUnderlyingTokenAccount: Pubkey, + + authority: Pubkey, + + userUnderlyingTokenAccount: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + question, + + vault, + + vaultUnderlyingTokenAccount, + + authority, + + userUnderlyingTokenAccount, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for SplitTokens + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct SplitTokensInstructionData { + pub amount: u64, + } + + impl SplitTokensInstructionData { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + /// Implementation for SplitTokensInstruction + impl SplitTokensInstruction { + fn discriminator() -> [u8; 8] { + [79u8, 195u8, 116u8, 0u8, 140u8, 176u8, 73u8, 179u8] + } + + pub fn data(data: SplitTokensInstructionData) -> Self { + Self { + accounts: SplitTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: SplitTokensInstructionAccounts) -> Self { + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.vault = AccountMeta::new(accounts.vault, false); + + self.accounts.vaultUnderlyingTokenAccount = + AccountMeta::new(accounts.vaultUnderlyingTokenAccount, false); + + self.accounts.authority = AccountMeta::new_readonly(accounts.authority, true); + + self.accounts.userUnderlyingTokenAccount = + AccountMeta::new(accounts.userUnderlyingTokenAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.vault.clone()); + + metas.push(self.accounts.vaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.userUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: MergeTokens + // .................................................................... + + /// Main instruction struct for MergeTokens + pub struct MergeTokensInstruction { + pub accounts: MergeTokensInstructionAccountMetas, + pub data: MergeTokensInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for MergeTokens instruction + #[derive(Debug, Clone, Default)] + pub struct MergeTokensInstructionAccountMetas { + pub question: AccountMeta, + + pub vault: AccountMeta, + + pub vaultUnderlyingTokenAccount: AccountMeta, + + pub authority: AccountMeta, + + pub userUnderlyingTokenAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for MergeTokens instruction + #[derive(Debug, Clone)] + pub struct MergeTokensInstructionAccounts { + pub question: Pubkey, + + pub vault: Pubkey, + + pub vaultUnderlyingTokenAccount: Pubkey, + + pub authority: Pubkey, + + pub userUnderlyingTokenAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl MergeTokensInstructionAccounts { + pub fn new( + question: Pubkey, + + vault: Pubkey, + + vaultUnderlyingTokenAccount: Pubkey, + + authority: Pubkey, + + userUnderlyingTokenAccount: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + question, + + vault, + + vaultUnderlyingTokenAccount, + + authority, + + userUnderlyingTokenAccount, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for MergeTokens + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct MergeTokensInstructionData { + pub amount: u64, + } + + impl MergeTokensInstructionData { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + /// Implementation for MergeTokensInstruction + impl MergeTokensInstruction { + fn discriminator() -> [u8; 8] { + [226u8, 89u8, 251u8, 121u8, 225u8, 130u8, 180u8, 14u8] + } + + pub fn data(data: MergeTokensInstructionData) -> Self { + Self { + accounts: MergeTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: MergeTokensInstructionAccounts) -> Self { + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.vault = AccountMeta::new(accounts.vault, false); + + self.accounts.vaultUnderlyingTokenAccount = + AccountMeta::new(accounts.vaultUnderlyingTokenAccount, false); + + self.accounts.authority = AccountMeta::new_readonly(accounts.authority, true); + + self.accounts.userUnderlyingTokenAccount = + AccountMeta::new(accounts.userUnderlyingTokenAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.vault.clone()); + + metas.push(self.accounts.vaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.userUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: RedeemTokens + // .................................................................... + + /// Main instruction struct for RedeemTokens + pub struct RedeemTokensInstruction { + pub accounts: RedeemTokensInstructionAccountMetas, + pub data: RedeemTokensInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for RedeemTokens instruction + #[derive(Debug, Clone, Default)] + pub struct RedeemTokensInstructionAccountMetas { + pub question: AccountMeta, + + pub vault: AccountMeta, + + pub vaultUnderlyingTokenAccount: AccountMeta, + + pub authority: AccountMeta, + + pub userUnderlyingTokenAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for RedeemTokens instruction + #[derive(Debug, Clone)] + pub struct RedeemTokensInstructionAccounts { + pub question: Pubkey, + + pub vault: Pubkey, + + pub vaultUnderlyingTokenAccount: Pubkey, + + pub authority: Pubkey, + + pub userUnderlyingTokenAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl RedeemTokensInstructionAccounts { + pub fn new( + question: Pubkey, + + vault: Pubkey, + + vaultUnderlyingTokenAccount: Pubkey, + + authority: Pubkey, + + userUnderlyingTokenAccount: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + question, + + vault, + + vaultUnderlyingTokenAccount, + + authority, + + userUnderlyingTokenAccount, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for RedeemTokens + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct RedeemTokensInstructionData {} + + impl RedeemTokensInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for RedeemTokensInstruction + impl RedeemTokensInstruction { + fn discriminator() -> [u8; 8] { + [246u8, 98u8, 134u8, 41u8, 152u8, 33u8, 120u8, 69u8] + } + + pub fn data(data: RedeemTokensInstructionData) -> Self { + Self { + accounts: RedeemTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: RedeemTokensInstructionAccounts) -> Self { + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.vault = AccountMeta::new(accounts.vault, false); + + self.accounts.vaultUnderlyingTokenAccount = + AccountMeta::new(accounts.vaultUnderlyingTokenAccount, false); + + self.accounts.authority = AccountMeta::new_readonly(accounts.authority, true); + + self.accounts.userUnderlyingTokenAccount = + AccountMeta::new(accounts.userUnderlyingTokenAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.vault.clone()); + + metas.push(self.accounts.vaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.userUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: AddMetadataToConditionalTokens + // .................................................................... + + /// Main instruction struct for AddMetadataToConditionalTokens + pub struct AddMetadataToConditionalTokensInstruction { + pub accounts: AddMetadataToConditionalTokensInstructionAccountMetas, + pub data: AddMetadataToConditionalTokensInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for AddMetadataToConditionalTokens instruction + #[derive(Debug, Clone, Default)] + pub struct AddMetadataToConditionalTokensInstructionAccountMetas { + pub payer: AccountMeta, + + pub vault: AccountMeta, + + pub conditionalTokenMint: AccountMeta, + + pub conditionalTokenMetadata: AccountMeta, + + pub tokenMetadataProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub rent: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for AddMetadataToConditionalTokens instruction + #[derive(Debug, Clone)] + pub struct AddMetadataToConditionalTokensInstructionAccounts { + pub payer: Pubkey, + + pub vault: Pubkey, + + pub conditionalTokenMint: Pubkey, + + pub conditionalTokenMetadata: Pubkey, + + pub tokenMetadataProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub rent: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl AddMetadataToConditionalTokensInstructionAccounts { + pub fn new( + payer: Pubkey, + + vault: Pubkey, + + conditionalTokenMint: Pubkey, + + conditionalTokenMetadata: Pubkey, + + tokenMetadataProgram: Pubkey, + + systemProgram: Pubkey, + + rent: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + payer, + + vault, + + conditionalTokenMint, + + conditionalTokenMetadata, + + tokenMetadataProgram, + + systemProgram, + + rent, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for AddMetadataToConditionalTokens + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct AddMetadataToConditionalTokensInstructionData { + pub args: AddMetadataToConditionalTokensArgs, + } + + impl AddMetadataToConditionalTokensInstructionData { + pub fn new(args: AddMetadataToConditionalTokensArgs) -> Self { + Self { args } + } + } + + /// Implementation for AddMetadataToConditionalTokensInstruction + impl AddMetadataToConditionalTokensInstruction { + fn discriminator() -> [u8; 8] { + [133u8, 20u8, 169u8, 231u8, 114u8, 112u8, 45u8, 1u8] + } + + pub fn data(data: AddMetadataToConditionalTokensInstructionData) -> Self { + Self { + accounts: AddMetadataToConditionalTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: AddMetadataToConditionalTokensInstructionAccounts, + ) -> Self { + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.vault = AccountMeta::new(accounts.vault, false); + + self.accounts.conditionalTokenMint = + AccountMeta::new_readonly(accounts.conditionalTokenMint, false); + + self.accounts.conditionalTokenMetadata = + AccountMeta::new(accounts.conditionalTokenMetadata, false); + + self.accounts.tokenMetadataProgram = + AccountMeta::new_readonly(accounts.tokenMetadataProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.rent = AccountMeta::new_readonly(accounts.rent, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.vault.clone()); + + metas.push(self.accounts.conditionalTokenMint.clone()); + + metas.push(self.accounts.conditionalTokenMetadata.clone()); + + metas.push(self.accounts.tokenMetadataProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.rent.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // ------------------------------------------------------------------------ + // Data Accounts (with discriminators) + // ------------------------------------------------------------------------ + + /// AccountDiscriminator implementation for ConditionalVault + impl AccountDiscriminator for ConditionalVault { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for Question + impl AccountDiscriminator for Question { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + // ------------------------------------------------------------------------ + // Errors + // ------------------------------------------------------------------------ + + /// Program errors for conditional_vault + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum ConditionalvaultError { + /// An assertion failed + AssertFailed = 6000, + + /// Insufficient underlying token balance to mint this amount of + /// conditional tokens + InsufficientUnderlyingTokens = 6001, + + /// Insufficient conditional token balance to merge this `amount` + InsufficientConditionalTokens = 6002, + + /// This `vault_underlying_token_account` is not this vault's + /// `underlying_token_account` + InvalidVaultUnderlyingTokenAccount = 6003, + + /// This conditional token mint is not this vault's conditional token + /// mint + InvalidConditionalTokenMint = 6004, + + /// Question needs to be resolved before users can redeem conditional + /// tokens for underlying tokens + CantRedeemConditionalTokens = 6005, + + /// Questions need 2 or more conditions + InsufficientNumConditions = 6006, + + /// Invalid number of payout numerators + InvalidNumPayoutNumerators = 6007, + + /// Client needs to pass in the list of conditional mints for a vault + /// followed by the user's token accounts for those tokens + InvalidConditionals = 6008, + + /// Conditional mint not in vault + ConditionalMintMismatch = 6009, + + /// Unable to deserialize a conditional token mint + BadConditionalMint = 6010, + + /// Unable to deserialize a conditional token account + BadConditionalTokenAccount = 6011, + + /// User conditional token account mint does not match conditional mint + ConditionalTokenMintMismatch = 6012, + + /// Payouts must sum to 1 or more + PayoutZero = 6013, + + /// Question already resolved + QuestionAlreadyResolved = 6014, + + /// Conditional token metadata already set + ConditionalTokenMetadataAlreadySet = 6015, + + /// Conditional token account is not owned by the authority + UnauthorizedConditionalTokenAccount = 6016, + + /// Payout numerators are too large, causing an overflow + InvalidPayoutNumerators = 6017, + + /// Questions can only have up to 10 outcomes + TooManyOutcomes = 6018, + } + + impl ConditionalvaultError { + /// Get the error code + pub fn code(&self) -> u32 { + *self as u32 + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + match self { + + Self::AssertFailed => "An assertion failed", + + Self::InsufficientUnderlyingTokens => "Insufficient underlying token balance to mint this amount of conditional tokens", + + Self::InsufficientConditionalTokens => "Insufficient conditional token balance to merge this `amount`", + + Self::InvalidVaultUnderlyingTokenAccount => "This `vault_underlying_token_account` is not this vault's `underlying_token_account`", + + Self::InvalidConditionalTokenMint => "This conditional token mint is not this vault's conditional token mint", + + Self::CantRedeemConditionalTokens => "Question needs to be resolved before users can redeem conditional tokens for underlying tokens", + + Self::InsufficientNumConditions => "Questions need 2 or more conditions", + + Self::InvalidNumPayoutNumerators => "Invalid number of payout numerators", + + Self::InvalidConditionals => "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens", + + Self::ConditionalMintMismatch => "Conditional mint not in vault", + + Self::BadConditionalMint => "Unable to deserialize a conditional token mint", + + Self::BadConditionalTokenAccount => "Unable to deserialize a conditional token account", + + Self::ConditionalTokenMintMismatch => "User conditional token account mint does not match conditional mint", + + Self::PayoutZero => "Payouts must sum to 1 or more", + + Self::QuestionAlreadyResolved => "Question already resolved", + + Self::ConditionalTokenMetadataAlreadySet => "Conditional token metadata already set", + + Self::UnauthorizedConditionalTokenAccount => "Conditional token account is not owned by the authority", + + Self::InvalidPayoutNumerators => "Payout numerators are too large, causing an overflow", + + Self::TooManyOutcomes => "Questions can only have up to 10 outcomes", + + } + } + + /// Try to convert from error code + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::AssertFailed), + + 6001 => Some(Self::InsufficientUnderlyingTokens), + + 6002 => Some(Self::InsufficientConditionalTokens), + + 6003 => Some(Self::InvalidVaultUnderlyingTokenAccount), + + 6004 => Some(Self::InvalidConditionalTokenMint), + + 6005 => Some(Self::CantRedeemConditionalTokens), + + 6006 => Some(Self::InsufficientNumConditions), + + 6007 => Some(Self::InvalidNumPayoutNumerators), + + 6008 => Some(Self::InvalidConditionals), + + 6009 => Some(Self::ConditionalMintMismatch), + + 6010 => Some(Self::BadConditionalMint), + + 6011 => Some(Self::BadConditionalTokenAccount), + + 6012 => Some(Self::ConditionalTokenMintMismatch), + + 6013 => Some(Self::PayoutZero), + + 6014 => Some(Self::QuestionAlreadyResolved), + + 6015 => Some(Self::ConditionalTokenMetadataAlreadySet), + + 6016 => Some(Self::UnauthorizedConditionalTokenAccount), + + 6017 => Some(Self::InvalidPayoutNumerators), + + 6018 => Some(Self::TooManyOutcomes), + + _ => None, + } + } + } + + impl std::fmt::Display for ConditionalvaultError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.code(), self.msg()) + } + } + + impl std::error::Error for ConditionalvaultError {} + + // ------------------------------------------------------------------------ + // Composite Accounts + // ------------------------------------------------------------------------ + + // ------------------------------------------------------------------------ + // Custom Types + // ------------------------------------------------------------------------ + + /// Custom struct: CommonFields + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct CommonFields { + pub slot: u64, + + pub unixTimestamp: i64, + } + + impl CommonFields { + pub fn new(slot: u64, unixTimestamp: i64) -> Self { + Self { + slot, + + unixTimestamp, + } + } + } + + /// Custom struct: AddMetadataToConditionalTokensArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct AddMetadataToConditionalTokensArgs { + pub name: String, + + pub symbol: String, + + pub uri: String, + } + + impl AddMetadataToConditionalTokensArgs { + pub fn new(name: String, symbol: String, uri: String) -> Self { + Self { name, symbol, uri } + } + } + + /// Custom struct: InitializeQuestionArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitializeQuestionArgs { + pub questionId: [u8; 32], + + pub oracle: Pubkey, + + pub numOutcomes: u8, + } + + impl InitializeQuestionArgs { + pub fn new(questionId: [u8; 32], oracle: Pubkey, numOutcomes: u8) -> Self { + Self { + questionId, + + oracle, + + numOutcomes, + } + } + } + + /// Custom struct: ResolveQuestionArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ResolveQuestionArgs { + pub payoutNumerators: Vec, + } + + impl ResolveQuestionArgs { + pub fn new(payoutNumerators: Vec) -> Self { + Self { payoutNumerators } + } + } + + /// Custom struct: ConditionalVault + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ConditionalVault { + pub question: Pubkey, + + pub underlyingTokenMint: Pubkey, + + pub underlyingTokenAccount: Pubkey, + + pub conditionalTokenMints: Vec, + + pub pdaBump: u8, + + pub decimals: u8, + + pub seqNum: u64, + } + + impl ConditionalVault { + pub fn new( + question: Pubkey, + + underlyingTokenMint: Pubkey, + + underlyingTokenAccount: Pubkey, + + conditionalTokenMints: Vec, + + pdaBump: u8, + + decimals: u8, + + seqNum: u64, + ) -> Self { + Self { + question, + + underlyingTokenMint, + + underlyingTokenAccount, + + conditionalTokenMints, + + pdaBump, + + decimals, + + seqNum, + } + } + } + + /// Custom struct: Question + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Question { + pub questionId: [u8; 32], + + pub oracle: Pubkey, + + pub payoutNumerators: Vec, + + pub payoutDenominator: u32, + } + + impl Question { + pub fn new( + questionId: [u8; 32], + + oracle: Pubkey, + + payoutNumerators: Vec, + + payoutDenominator: u32, + ) -> Self { + Self { + questionId, + + oracle, + + payoutNumerators, + + payoutDenominator, + } + } + } +} + +// ---------------------------------------------------------------------------- +// Program: launchpad_v_7 +// ---------------------------------------------------------------------------- +pub mod launchpad_v_7 { + use super::*; + + // ------------------------------------------------------------------------ + // Program ID + // ------------------------------------------------------------------------ + + /// Returns the program ID for launchpad_v7 + pub fn program_id() -> Pubkey { + pubkey!("moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM") + } + + // ------------------------------------------------------------------------ + // Instructions + // ------------------------------------------------------------------------ + + // .................................................................... + // Instruction: InitializeLaunch + // .................................................................... + + /// Main instruction struct for InitializeLaunch + pub struct InitializeLaunchInstruction { + pub accounts: InitializeLaunchInstructionAccountMetas, + pub data: InitializeLaunchInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeLaunch instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeLaunchInstructionAccountMetas { + pub launch: AccountMeta, + + pub baseMint: AccountMeta, + + pub tokenMetadata: AccountMeta, + + pub launchSigner: AccountMeta, + + pub quoteVault: AccountMeta, + + pub baseVault: AccountMeta, + + pub payer: AccountMeta, + + pub launchAuthority: AccountMeta, + + pub quoteMint: AccountMeta, + + pub additionalTokensRecipient: AccountMeta, + + pub rent: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenMetadataProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeLaunch instruction + #[derive(Debug, Clone)] + pub struct InitializeLaunchInstructionAccounts { + pub launch: Pubkey, + + pub baseMint: Pubkey, + + pub tokenMetadata: Pubkey, + + pub launchSigner: Pubkey, + + pub quoteVault: Pubkey, + + pub baseVault: Pubkey, + + pub payer: Pubkey, + + pub launchAuthority: Pubkey, + + pub quoteMint: Pubkey, + + pub additionalTokensRecipient: Pubkey, + + pub rent: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenMetadataProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeLaunchInstructionAccounts { + pub fn new( + launch: Pubkey, + + baseMint: Pubkey, + + tokenMetadata: Pubkey, + + launchSigner: Pubkey, + + quoteVault: Pubkey, + + baseVault: Pubkey, + + payer: Pubkey, + + launchAuthority: Pubkey, + + quoteMint: Pubkey, + + additionalTokensRecipient: Pubkey, + + rent: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + systemProgram: Pubkey, + + tokenMetadataProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + baseMint, + + tokenMetadata, + + launchSigner, + + quoteVault, + + baseVault, + + payer, + + launchAuthority, + + quoteMint, + + additionalTokensRecipient, + + rent, + + tokenProgram, + + associatedTokenProgram, + + systemProgram, + + tokenMetadataProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeLaunchInstructionData { + pub args: InitializeLaunchArgs, + } + + impl InitializeLaunchInstructionData { + pub fn new(args: InitializeLaunchArgs) -> Self { + Self { args } + } + } + + /// Implementation for InitializeLaunchInstruction + impl InitializeLaunchInstruction { + fn discriminator() -> [u8; 8] { + [90u8, 201u8, 220u8, 142u8, 112u8, 253u8, 100u8, 13u8] + } + + pub fn data(data: InitializeLaunchInstructionData) -> Self { + Self { + accounts: InitializeLaunchInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeLaunchInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.tokenMetadata = AccountMeta::new(accounts.tokenMetadata, false); + + self.accounts.launchSigner = AccountMeta::new_readonly(accounts.launchSigner, false); + + self.accounts.quoteVault = AccountMeta::new(accounts.quoteVault, false); + + self.accounts.baseVault = AccountMeta::new(accounts.baseVault, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.launchAuthority = + AccountMeta::new_readonly(accounts.launchAuthority, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.additionalTokensRecipient = + AccountMeta::new_readonly(accounts.additionalTokensRecipient, false); + + self.accounts.rent = AccountMeta::new_readonly(accounts.rent, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenMetadataProgram = + AccountMeta::new_readonly(accounts.tokenMetadataProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.tokenMetadata.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.launchAuthority.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.additionalTokensRecipient.clone()); + + metas.push(self.accounts.rent.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenMetadataProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: StartLaunch + // .................................................................... + + /// Main instruction struct for StartLaunch + pub struct StartLaunchInstruction { + pub accounts: StartLaunchInstructionAccountMetas, + pub data: StartLaunchInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for StartLaunch instruction + #[derive(Debug, Clone, Default)] + pub struct StartLaunchInstructionAccountMetas { + pub launch: AccountMeta, + + pub launchAuthority: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for StartLaunch instruction + #[derive(Debug, Clone)] + pub struct StartLaunchInstructionAccounts { + pub launch: Pubkey, + + pub launchAuthority: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl StartLaunchInstructionAccounts { + pub fn new( + launch: Pubkey, + + launchAuthority: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + launchAuthority, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for StartLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct StartLaunchInstructionData {} + + impl StartLaunchInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for StartLaunchInstruction + impl StartLaunchInstruction { + fn discriminator() -> [u8; 8] { + [205u8, 178u8, 36u8, 232u8, 95u8, 202u8, 241u8, 68u8] + } + + pub fn data(data: StartLaunchInstructionData) -> Self { + Self { + accounts: StartLaunchInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: StartLaunchInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.launchAuthority = + AccountMeta::new_readonly(accounts.launchAuthority, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.launchAuthority.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: Fund + // .................................................................... + + /// Main instruction struct for Fund + pub struct FundInstruction { + pub accounts: FundInstructionAccountMetas, + pub data: FundInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for Fund instruction + #[derive(Debug, Clone, Default)] + pub struct FundInstructionAccountMetas { + pub launch: AccountMeta, + + pub fundingRecord: AccountMeta, + + pub launchSigner: AccountMeta, + + pub launchQuoteVault: AccountMeta, + + pub funder: AccountMeta, + + pub payer: AccountMeta, + + pub funderQuoteAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for Fund instruction + #[derive(Debug, Clone)] + pub struct FundInstructionAccounts { + pub launch: Pubkey, + + pub fundingRecord: Pubkey, + + pub launchSigner: Pubkey, + + pub launchQuoteVault: Pubkey, + + pub funder: Pubkey, + + pub payer: Pubkey, + + pub funderQuoteAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl FundInstructionAccounts { + pub fn new( + launch: Pubkey, + + fundingRecord: Pubkey, + + launchSigner: Pubkey, + + launchQuoteVault: Pubkey, + + funder: Pubkey, + + payer: Pubkey, + + funderQuoteAccount: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + fundingRecord, + + launchSigner, + + launchQuoteVault, + + funder, + + payer, + + funderQuoteAccount, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for Fund + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct FundInstructionData { + pub amount: u64, + } + + impl FundInstructionData { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + /// Implementation for FundInstruction + impl FundInstruction { + fn discriminator() -> [u8; 8] { + [218u8, 188u8, 111u8, 221u8, 152u8, 113u8, 174u8, 7u8] + } + + pub fn data(data: FundInstructionData) -> Self { + Self { + accounts: FundInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: FundInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.fundingRecord = AccountMeta::new(accounts.fundingRecord, false); + + self.accounts.launchSigner = AccountMeta::new_readonly(accounts.launchSigner, false); + + self.accounts.launchQuoteVault = AccountMeta::new(accounts.launchQuoteVault, false); + + self.accounts.funder = AccountMeta::new_readonly(accounts.funder, true); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.funderQuoteAccount = AccountMeta::new(accounts.funderQuoteAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.fundingRecord.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.launchQuoteVault.clone()); + + metas.push(self.accounts.funder.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.funderQuoteAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: SetFundingRecordApproval + // .................................................................... + + /// Main instruction struct for SetFundingRecordApproval + pub struct SetFundingRecordApprovalInstruction { + pub accounts: SetFundingRecordApprovalInstructionAccountMetas, + pub data: SetFundingRecordApprovalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for SetFundingRecordApproval instruction + #[derive(Debug, Clone, Default)] + pub struct SetFundingRecordApprovalInstructionAccountMetas { + pub launch: AccountMeta, + + pub fundingRecord: AccountMeta, + + pub launchAuthority: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for SetFundingRecordApproval instruction + #[derive(Debug, Clone)] + pub struct SetFundingRecordApprovalInstructionAccounts { + pub launch: Pubkey, + + pub fundingRecord: Pubkey, + + pub launchAuthority: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl SetFundingRecordApprovalInstructionAccounts { + pub fn new( + launch: Pubkey, + + fundingRecord: Pubkey, + + launchAuthority: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + fundingRecord, + + launchAuthority, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for SetFundingRecordApproval + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct SetFundingRecordApprovalInstructionData { + pub approvedAmount: u64, + } + + impl SetFundingRecordApprovalInstructionData { + pub fn new(approvedAmount: u64) -> Self { + Self { approvedAmount } + } + } + + /// Implementation for SetFundingRecordApprovalInstruction + impl SetFundingRecordApprovalInstruction { + fn discriminator() -> [u8; 8] { + [18u8, 197u8, 233u8, 219u8, 37u8, 33u8, 186u8, 25u8] + } + + pub fn data(data: SetFundingRecordApprovalInstructionData) -> Self { + Self { + accounts: SetFundingRecordApprovalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: SetFundingRecordApprovalInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.fundingRecord = AccountMeta::new(accounts.fundingRecord, false); + + self.accounts.launchAuthority = + AccountMeta::new_readonly(accounts.launchAuthority, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.fundingRecord.clone()); + + metas.push(self.accounts.launchAuthority.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CompleteLaunch + // .................................................................... + + /// Main instruction struct for CompleteLaunch + pub struct CompleteLaunchInstruction { + pub accounts: CompleteLaunchInstructionAccountMetas, + pub data: CompleteLaunchInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CompleteLaunch instruction + #[derive(Debug, Clone, Default)] + pub struct CompleteLaunchInstructionAccountMetas { + pub launch: AccountMeta, + + pub launchAuthority: AccountMeta, + + pub tokenMetadata: AccountMeta, + + pub payer: AccountMeta, + + pub launchSigner: AccountMeta, + + pub launchQuoteVault: AccountMeta, + + pub launchBaseVault: AccountMeta, + + pub treasuryQuoteAccount: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub daoOwnedLpPosition: AccountMeta, + + pub futarchyAmmBaseVault: AccountMeta, + + pub futarchyAmmQuoteVault: AccountMeta, + + pub dao: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigVault: AccountMeta, + + pub spendingLimit: AccountMeta, + + pub bidWall: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub feeRecipient: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub staticAccounts: StaticAccountsInstructionAccountMetas, + + pub meteoraAccounts: MeteoraAccountsInstructionAccountMetas, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CompleteLaunch instruction + #[derive(Debug, Clone)] + pub struct CompleteLaunchInstructionAccounts { + pub launch: Pubkey, + + pub launchAuthority: Pubkey, + + pub tokenMetadata: Pubkey, + + pub payer: Pubkey, + + pub launchSigner: Pubkey, + + pub launchQuoteVault: Pubkey, + + pub launchBaseVault: Pubkey, + + pub treasuryQuoteAccount: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub daoOwnedLpPosition: Pubkey, + + pub futarchyAmmBaseVault: Pubkey, + + pub futarchyAmmQuoteVault: Pubkey, + + pub dao: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub spendingLimit: Pubkey, + + pub bidWall: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub feeRecipient: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub staticAccounts: StaticAccountsInstructionAccounts, + + pub meteoraAccounts: MeteoraAccountsInstructionAccounts, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CompleteLaunchInstructionAccounts { + pub fn new( + launch: Pubkey, + + launchAuthority: Pubkey, + + tokenMetadata: Pubkey, + + payer: Pubkey, + + launchSigner: Pubkey, + + launchQuoteVault: Pubkey, + + launchBaseVault: Pubkey, + + treasuryQuoteAccount: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + daoOwnedLpPosition: Pubkey, + + futarchyAmmBaseVault: Pubkey, + + futarchyAmmQuoteVault: Pubkey, + + dao: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigVault: Pubkey, + + spendingLimit: Pubkey, + + bidWall: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + feeRecipient: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + staticAccounts: StaticAccountsInstructionAccounts, + + meteoraAccounts: MeteoraAccountsInstructionAccounts, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + launchAuthority, + + tokenMetadata, + + payer, + + launchSigner, + + launchQuoteVault, + + launchBaseVault, + + treasuryQuoteAccount, + + baseMint, + + quoteMint, + + daoOwnedLpPosition, + + futarchyAmmBaseVault, + + futarchyAmmQuoteVault, + + dao, + + squadsMultisig, + + squadsMultisigVault, + + spendingLimit, + + bidWall, + + bidWallQuoteTokenAccount, + + feeRecipient, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + staticAccounts, + + meteoraAccounts, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CompleteLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CompleteLaunchInstructionData {} + + impl CompleteLaunchInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CompleteLaunchInstruction + impl CompleteLaunchInstruction { + fn discriminator() -> [u8; 8] { + [118u8, 207u8, 250u8, 130u8, 44u8, 148u8, 251u8, 237u8] + } + + pub fn data(data: CompleteLaunchInstructionData) -> Self { + Self { + accounts: CompleteLaunchInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CompleteLaunchInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.launchAuthority = + AccountMeta::new_readonly(accounts.launchAuthority, true); + + self.accounts.tokenMetadata = AccountMeta::new(accounts.tokenMetadata, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.launchSigner = AccountMeta::new(accounts.launchSigner, false); + + self.accounts.launchQuoteVault = AccountMeta::new(accounts.launchQuoteVault, false); + + self.accounts.launchBaseVault = AccountMeta::new(accounts.launchBaseVault, false); + + self.accounts.treasuryQuoteAccount = + AccountMeta::new(accounts.treasuryQuoteAccount, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.daoOwnedLpPosition = AccountMeta::new(accounts.daoOwnedLpPosition, false); + + self.accounts.futarchyAmmBaseVault = + AccountMeta::new(accounts.futarchyAmmBaseVault, false); + + self.accounts.futarchyAmmQuoteVault = + AccountMeta::new(accounts.futarchyAmmQuoteVault, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.squadsMultisig = AccountMeta::new(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigVault = + AccountMeta::new_readonly(accounts.squadsMultisigVault, false); + + self.accounts.spendingLimit = AccountMeta::new(accounts.spendingLimit, false); + + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.feeRecipient = AccountMeta::new_readonly(accounts.feeRecipient, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts + .staticAccounts + .set_from_accounts(accounts.staticAccounts); + + self.accounts + .meteoraAccounts + .set_from_accounts(accounts.meteoraAccounts); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.launchAuthority.clone()); + + metas.push(self.accounts.tokenMetadata.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.launchQuoteVault.clone()); + + metas.push(self.accounts.launchBaseVault.clone()); + + metas.push(self.accounts.treasuryQuoteAccount.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.daoOwnedLpPosition.clone()); + + metas.push(self.accounts.futarchyAmmBaseVault.clone()); + + metas.push(self.accounts.futarchyAmmQuoteVault.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigVault.clone()); + + metas.push(self.accounts.spendingLimit.clone()); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.feeRecipient.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.extend(self.accounts.staticAccounts.to_account_metas()); + + metas.extend(self.accounts.meteoraAccounts.to_account_metas()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: Refund + // .................................................................... + + /// Main instruction struct for Refund + pub struct RefundInstruction { + pub accounts: RefundInstructionAccountMetas, + pub data: RefundInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for Refund instruction + #[derive(Debug, Clone, Default)] + pub struct RefundInstructionAccountMetas { + pub launch: AccountMeta, + + pub fundingRecord: AccountMeta, + + pub launchQuoteVault: AccountMeta, + + pub launchSigner: AccountMeta, + + pub funder: AccountMeta, + + pub funderQuoteAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for Refund instruction + #[derive(Debug, Clone)] + pub struct RefundInstructionAccounts { + pub launch: Pubkey, + + pub fundingRecord: Pubkey, + + pub launchQuoteVault: Pubkey, + + pub launchSigner: Pubkey, + + pub funder: Pubkey, + + pub funderQuoteAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl RefundInstructionAccounts { + pub fn new( + launch: Pubkey, + + fundingRecord: Pubkey, + + launchQuoteVault: Pubkey, + + launchSigner: Pubkey, + + funder: Pubkey, + + funderQuoteAccount: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + fundingRecord, + + launchQuoteVault, + + launchSigner, + + funder, + + funderQuoteAccount, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for Refund + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct RefundInstructionData {} + + impl RefundInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for RefundInstruction + impl RefundInstruction { + fn discriminator() -> [u8; 8] { + [2u8, 96u8, 183u8, 251u8, 63u8, 208u8, 46u8, 46u8] + } + + pub fn data(data: RefundInstructionData) -> Self { + Self { + accounts: RefundInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: RefundInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.fundingRecord = AccountMeta::new(accounts.fundingRecord, false); + + self.accounts.launchQuoteVault = AccountMeta::new(accounts.launchQuoteVault, false); + + self.accounts.launchSigner = AccountMeta::new_readonly(accounts.launchSigner, false); + + self.accounts.funder = AccountMeta::new_readonly(accounts.funder, false); + + self.accounts.funderQuoteAccount = AccountMeta::new(accounts.funderQuoteAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.fundingRecord.clone()); + + metas.push(self.accounts.launchQuoteVault.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.funder.clone()); + + metas.push(self.accounts.funderQuoteAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: Claim + // .................................................................... + + /// Main instruction struct for Claim + pub struct ClaimInstruction { + pub accounts: ClaimInstructionAccountMetas, + pub data: ClaimInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for Claim instruction + #[derive(Debug, Clone, Default)] + pub struct ClaimInstructionAccountMetas { + pub launch: AccountMeta, + + pub fundingRecord: AccountMeta, + + pub launchSigner: AccountMeta, + + pub baseMint: AccountMeta, + + pub launchBaseVault: AccountMeta, + + pub funder: AccountMeta, + + pub funderTokenAccount: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for Claim instruction + #[derive(Debug, Clone)] + pub struct ClaimInstructionAccounts { + pub launch: Pubkey, + + pub fundingRecord: Pubkey, + + pub launchSigner: Pubkey, + + pub baseMint: Pubkey, + + pub launchBaseVault: Pubkey, + + pub funder: Pubkey, + + pub funderTokenAccount: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ClaimInstructionAccounts { + pub fn new( + launch: Pubkey, + + fundingRecord: Pubkey, + + launchSigner: Pubkey, + + baseMint: Pubkey, + + launchBaseVault: Pubkey, + + funder: Pubkey, + + funderTokenAccount: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + fundingRecord, + + launchSigner, + + baseMint, + + launchBaseVault, + + funder, + + funderTokenAccount, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for Claim + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ClaimInstructionData {} + + impl ClaimInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ClaimInstruction + impl ClaimInstruction { + fn discriminator() -> [u8; 8] { + [62u8, 198u8, 214u8, 193u8, 213u8, 159u8, 108u8, 210u8] + } + + pub fn data(data: ClaimInstructionData) -> Self { + Self { + accounts: ClaimInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ClaimInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.fundingRecord = AccountMeta::new(accounts.fundingRecord, false); + + self.accounts.launchSigner = AccountMeta::new_readonly(accounts.launchSigner, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.launchBaseVault = AccountMeta::new(accounts.launchBaseVault, false); + + self.accounts.funder = AccountMeta::new_readonly(accounts.funder, false); + + self.accounts.funderTokenAccount = AccountMeta::new(accounts.funderTokenAccount, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.fundingRecord.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.launchBaseVault.clone()); + + metas.push(self.accounts.funder.clone()); + + metas.push(self.accounts.funderTokenAccount.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CloseLaunch + // .................................................................... + + /// Main instruction struct for CloseLaunch + pub struct CloseLaunchInstruction { + pub accounts: CloseLaunchInstructionAccountMetas, + pub data: CloseLaunchInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CloseLaunch instruction + #[derive(Debug, Clone, Default)] + pub struct CloseLaunchInstructionAccountMetas { + pub launch: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CloseLaunch instruction + #[derive(Debug, Clone)] + pub struct CloseLaunchInstructionAccounts { + pub launch: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CloseLaunchInstructionAccounts { + pub fn new(launch: Pubkey, eventAuthority: Pubkey, program: Pubkey) -> Self { + Self { + launch, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CloseLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CloseLaunchInstructionData {} + + impl CloseLaunchInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CloseLaunchInstruction + impl CloseLaunchInstruction { + fn discriminator() -> [u8; 8] { + [27u8, 216u8, 111u8, 223u8, 10u8, 230u8, 19u8, 211u8] + } + + pub fn data(data: CloseLaunchInstructionData) -> Self { + Self { + accounts: CloseLaunchInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CloseLaunchInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ClaimAdditionalTokenAllocation + // .................................................................... + + /// Main instruction struct for ClaimAdditionalTokenAllocation + pub struct ClaimAdditionalTokenAllocationInstruction { + pub accounts: ClaimAdditionalTokenAllocationInstructionAccountMetas, + pub data: ClaimAdditionalTokenAllocationInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ClaimAdditionalTokenAllocation instruction + #[derive(Debug, Clone, Default)] + pub struct ClaimAdditionalTokenAllocationInstructionAccountMetas { + pub launch: AccountMeta, + + pub payer: AccountMeta, + + pub launchSigner: AccountMeta, + + pub launchBaseVault: AccountMeta, + + pub baseMint: AccountMeta, + + pub additionalTokensRecipient: AccountMeta, + + pub additionalTokensRecipientTokenAccount: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ClaimAdditionalTokenAllocation instruction + #[derive(Debug, Clone)] + pub struct ClaimAdditionalTokenAllocationInstructionAccounts { + pub launch: Pubkey, + + pub payer: Pubkey, + + pub launchSigner: Pubkey, + + pub launchBaseVault: Pubkey, + + pub baseMint: Pubkey, + + pub additionalTokensRecipient: Pubkey, + + pub additionalTokensRecipientTokenAccount: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ClaimAdditionalTokenAllocationInstructionAccounts { + pub fn new( + launch: Pubkey, + + payer: Pubkey, + + launchSigner: Pubkey, + + launchBaseVault: Pubkey, + + baseMint: Pubkey, + + additionalTokensRecipient: Pubkey, + + additionalTokensRecipientTokenAccount: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + payer, + + launchSigner, + + launchBaseVault, + + baseMint, + + additionalTokensRecipient, + + additionalTokensRecipientTokenAccount, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ClaimAdditionalTokenAllocation + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ClaimAdditionalTokenAllocationInstructionData {} + + impl ClaimAdditionalTokenAllocationInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ClaimAdditionalTokenAllocationInstruction + impl ClaimAdditionalTokenAllocationInstruction { + fn discriminator() -> [u8; 8] { + [245u8, 214u8, 225u8, 140u8, 237u8, 135u8, 163u8, 244u8] + } + + pub fn data(data: ClaimAdditionalTokenAllocationInstructionData) -> Self { + Self { + accounts: ClaimAdditionalTokenAllocationInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: ClaimAdditionalTokenAllocationInstructionAccounts, + ) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.launchSigner = AccountMeta::new(accounts.launchSigner, false); + + self.accounts.launchBaseVault = AccountMeta::new(accounts.launchBaseVault, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.additionalTokensRecipient = + AccountMeta::new_readonly(accounts.additionalTokensRecipient, false); + + self.accounts.additionalTokensRecipientTokenAccount = + AccountMeta::new(accounts.additionalTokensRecipientTokenAccount, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.launchBaseVault.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.additionalTokensRecipient.clone()); + + metas.push(self.accounts.additionalTokensRecipientTokenAccount.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: InitializePerformancePackage + // .................................................................... + + /// Main instruction struct for InitializePerformancePackage + pub struct InitializePerformancePackageInstruction { + pub accounts: InitializePerformancePackageInstructionAccountMetas, + pub data: InitializePerformancePackageInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializePerformancePackage instruction + #[derive(Debug, Clone, Default)] + pub struct InitializePerformancePackageInstructionAccountMetas { + pub launch: AccountMeta, + + pub payer: AccountMeta, + + pub launchSigner: AccountMeta, + + pub launchBaseVault: AccountMeta, + + pub baseMint: AccountMeta, + + pub dao: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigVault: AccountMeta, + + pub performancePackage: AccountMeta, + + pub performancePackageTokenAccount: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub squadsProgram: AccountMeta, + + pub priceBasedPerformancePackageProgram: AccountMeta, + + pub priceBasedPerformancePackageEventAuthority: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializePerformancePackage instruction + #[derive(Debug, Clone)] + pub struct InitializePerformancePackageInstructionAccounts { + pub launch: Pubkey, + + pub payer: Pubkey, + + pub launchSigner: Pubkey, + + pub launchBaseVault: Pubkey, + + pub baseMint: Pubkey, + + pub dao: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub performancePackage: Pubkey, + + pub performancePackageTokenAccount: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub squadsProgram: Pubkey, + + pub priceBasedPerformancePackageProgram: Pubkey, + + pub priceBasedPerformancePackageEventAuthority: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializePerformancePackageInstructionAccounts { + pub fn new( + launch: Pubkey, + + payer: Pubkey, + + launchSigner: Pubkey, + + launchBaseVault: Pubkey, + + baseMint: Pubkey, + + dao: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigVault: Pubkey, + + performancePackage: Pubkey, + + performancePackageTokenAccount: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + squadsProgram: Pubkey, + + priceBasedPerformancePackageProgram: Pubkey, + + priceBasedPerformancePackageEventAuthority: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + launch, + + payer, + + launchSigner, + + launchBaseVault, + + baseMint, + + dao, + + squadsMultisig, + + squadsMultisigVault, + + performancePackage, + + performancePackageTokenAccount, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + squadsProgram, + + priceBasedPerformancePackageProgram, + + priceBasedPerformancePackageEventAuthority, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializePerformancePackage + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializePerformancePackageInstructionData {} + + impl InitializePerformancePackageInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for InitializePerformancePackageInstruction + impl InitializePerformancePackageInstruction { + fn discriminator() -> [u8; 8] { + [3u8, 171u8, 56u8, 55u8, 135u8, 100u8, 163u8, 186u8] + } + + pub fn data(data: InitializePerformancePackageInstructionData) -> Self { + Self { + accounts: InitializePerformancePackageInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: InitializePerformancePackageInstructionAccounts, + ) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.launchSigner = AccountMeta::new(accounts.launchSigner, false); + + self.accounts.launchBaseVault = AccountMeta::new(accounts.launchBaseVault, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.dao = AccountMeta::new_readonly(accounts.dao, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigVault = + AccountMeta::new_readonly(accounts.squadsMultisigVault, false); + + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.performancePackageTokenAccount = + AccountMeta::new(accounts.performancePackageTokenAccount, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.squadsProgram = AccountMeta::new_readonly(accounts.squadsProgram, false); + + self.accounts.priceBasedPerformancePackageProgram = + AccountMeta::new_readonly(accounts.priceBasedPerformancePackageProgram, false); + + self.accounts.priceBasedPerformancePackageEventAuthority = AccountMeta::new_readonly( + accounts.priceBasedPerformancePackageEventAuthority, + false, + ); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.launchSigner.clone()); + + metas.push(self.accounts.launchBaseVault.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigVault.clone()); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.performancePackageTokenAccount.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.squadsProgram.clone()); + + metas.push(self.accounts.priceBasedPerformancePackageProgram.clone()); + + metas.push( + self.accounts + .priceBasedPerformancePackageEventAuthority + .clone(), + ); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ResizeFundingRecord + // .................................................................... + + /// Main instruction struct for ResizeFundingRecord + pub struct ResizeFundingRecordInstruction { + pub accounts: ResizeFundingRecordInstructionAccountMetas, + pub data: ResizeFundingRecordInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ResizeFundingRecord instruction + #[derive(Debug, Clone, Default)] + pub struct ResizeFundingRecordInstructionAccountMetas { + pub fundingRecord: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + } + + /// Account pubkeys for ResizeFundingRecord instruction + #[derive(Debug, Clone)] + pub struct ResizeFundingRecordInstructionAccounts { + pub fundingRecord: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + } + + impl ResizeFundingRecordInstructionAccounts { + pub fn new(fundingRecord: Pubkey, payer: Pubkey, systemProgram: Pubkey) -> Self { + Self { + fundingRecord, + + payer, + + systemProgram, + } + } + } + + /// Instruction data for ResizeFundingRecord + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ResizeFundingRecordInstructionData {} + + impl ResizeFundingRecordInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ResizeFundingRecordInstruction + impl ResizeFundingRecordInstruction { + fn discriminator() -> [u8; 8] { + [134u8, 103u8, 232u8, 47u8, 182u8, 183u8, 198u8, 5u8] + } + + pub fn data(data: ResizeFundingRecordInstructionData) -> Self { + Self { + accounts: ResizeFundingRecordInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ResizeFundingRecordInstructionAccounts) -> Self { + self.accounts.fundingRecord = AccountMeta::new(accounts.fundingRecord, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.fundingRecord.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ResizeLaunch + // .................................................................... + + /// Main instruction struct for ResizeLaunch + pub struct ResizeLaunchInstruction { + pub accounts: ResizeLaunchInstructionAccountMetas, + pub data: ResizeLaunchInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ResizeLaunch instruction + #[derive(Debug, Clone, Default)] + pub struct ResizeLaunchInstructionAccountMetas { + pub launch: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + } + + /// Account pubkeys for ResizeLaunch instruction + #[derive(Debug, Clone)] + pub struct ResizeLaunchInstructionAccounts { + pub launch: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + } + + impl ResizeLaunchInstructionAccounts { + pub fn new(launch: Pubkey, payer: Pubkey, systemProgram: Pubkey) -> Self { + Self { + launch, + + payer, + + systemProgram, + } + } + } + + /// Instruction data for ResizeLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ResizeLaunchInstructionData {} + + impl ResizeLaunchInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ResizeLaunchInstruction + impl ResizeLaunchInstruction { + fn discriminator() -> [u8; 8] { + [3u8, 23u8, 109u8, 180u8, 59u8, 58u8, 163u8, 211u8] + } + + pub fn data(data: ResizeLaunchInstructionData) -> Self { + Self { + accounts: ResizeLaunchInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ResizeLaunchInstructionAccounts) -> Self { + self.accounts.launch = AccountMeta::new(accounts.launch, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.launch.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // ------------------------------------------------------------------------ + // Data Accounts (with discriminators) + // ------------------------------------------------------------------------ + + /// AccountDiscriminator implementation for FundingRecord + impl AccountDiscriminator for FundingRecord { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for OldFundingRecord + impl AccountDiscriminator for OldFundingRecord { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for Launch + impl AccountDiscriminator for Launch { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for OldLaunch + impl AccountDiscriminator for OldLaunch { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + // ------------------------------------------------------------------------ + // Errors + // ------------------------------------------------------------------------ + + /// Program errors for launchpad_v7 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum Launchpadv7Error { + /// Invalid amount + InvalidAmount = 6000, + + /// Supply must be zero + SupplyNonZero = 6001, + + /// Launch period must be between 1 hour and 2 weeks + InvalidSecondsForLaunch = 6002, + + /// Insufficient funds + InsufficientFunds = 6003, + + /// Token mint key must end in 'meta' + InvalidTokenKey = 6004, + + /// Invalid launch state + InvalidLaunchState = 6005, + + /// Launch period not over + LaunchPeriodNotOver = 6006, + + /// Launch is complete, no more funding allowed + LaunchExpired = 6007, + + /// For you to get a refund, either the launch needs to be in a + /// refunding state or the launch must have been over-committed + LaunchNotRefunding = 6008, + + /// Launch must be initialized to be started + LaunchNotInitialized = 6009, + + /// Freeze authority can't be set on launchpad tokens + FreezeAuthoritySet = 6010, + + /// Monthly spending limit must be less than 1/6th of the minimum raise + /// amount and cannot be 0 + InvalidMonthlySpendingLimit = 6011, + + /// There can only be at most 10 monthly spending limit members + InvalidMonthlySpendingLimitMembers = 6012, + + /// Cannot do more than a 50% premine, minimum is 10 atoms of token + InvalidPriceBasedPremineAmount = 6013, + + /// Insiders must be forced to wait at least 18 months before unlocking + /// their tokens + InvalidPerformancePackageMinUnlockTime = 6014, + + /// Launch authority must be set to complete the launch until 2 days + /// after closing + LaunchAuthorityNotSet = 6015, + + /// The final amount raised must be greater than or equal to the minimum + /// raise amount + FinalRaiseAmountTooLow = 6016, + + /// Tokens already claimed + TokensAlreadyClaimed = 6017, + + /// Money already refunded + MoneyAlreadyRefunded = 6018, + + /// An invariant was violated. You should get in contact with the + /// MetaDAO team if you see this + InvariantViolated = 6019, + + /// Launch must be live to be closed + LaunchNotLive = 6020, + + /// Minimum raise amount must be greater than or equal to $0.5 so that + /// there's enough liquidity for the launch + InvalidMinimumRaiseAmount = 6021, + + /// The final raise amount has already been set + FinalRaiseAmountAlreadySet = 6022, + + /// Total approved amount must be greater than or equal to the minimum + /// raise amount + TotalApprovedAmountTooLow = 6023, + + /// Invalid additional tokens recipient - should be set if additional + /// tokens amount is greater than 0 + InvalidAdditionalTokensRecipient = 6024, + + /// No additional tokens recipient set + NoAdditionalTokensRecipientSet = 6025, + + /// Additional tokens already claimed + AdditionalTokensAlreadyClaimed = 6026, + + /// Funding record approval period is over + FundingRecordApprovalPeriodOver = 6027, + + /// Performance package already initialized + PerformancePackageAlreadyInitialized = 6028, + + /// Invalid DAO + InvalidDao = 6029, + + /// Accumulator activation delay must be less than the launch duration + InvalidAccumulatorActivationDelaySeconds = 6030, + } + + impl Launchpadv7Error { + /// Get the error code + pub fn code(&self) -> u32 { + *self as u32 + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + match self { + + Self::InvalidAmount => "Invalid amount", + + Self::SupplyNonZero => "Supply must be zero", + + Self::InvalidSecondsForLaunch => "Launch period must be between 1 hour and 2 weeks", + + Self::InsufficientFunds => "Insufficient funds", + + Self::InvalidTokenKey => "Token mint key must end in 'meta'", + + Self::InvalidLaunchState => "Invalid launch state", + + Self::LaunchPeriodNotOver => "Launch period not over", + + Self::LaunchExpired => "Launch is complete, no more funding allowed", + + Self::LaunchNotRefunding => "For you to get a refund, either the launch needs to be in a refunding state or the launch must have been over-committed", + + Self::LaunchNotInitialized => "Launch must be initialized to be started", + + Self::FreezeAuthoritySet => "Freeze authority can't be set on launchpad tokens", + + Self::InvalidMonthlySpendingLimit => "Monthly spending limit must be less than 1/6th of the minimum raise amount and cannot be 0", + + Self::InvalidMonthlySpendingLimitMembers => "There can only be at most 10 monthly spending limit members", + + Self::InvalidPriceBasedPremineAmount => "Cannot do more than a 50% premine, minimum is 10 atoms of token", + + Self::InvalidPerformancePackageMinUnlockTime => "Insiders must be forced to wait at least 18 months before unlocking their tokens", + + Self::LaunchAuthorityNotSet => "Launch authority must be set to complete the launch until 2 days after closing", + + Self::FinalRaiseAmountTooLow => "The final amount raised must be greater than or equal to the minimum raise amount", + + Self::TokensAlreadyClaimed => "Tokens already claimed", + + Self::MoneyAlreadyRefunded => "Money already refunded", + + Self::InvariantViolated => "An invariant was violated. You should get in contact with the MetaDAO team if you see this", + + Self::LaunchNotLive => "Launch must be live to be closed", + + Self::InvalidMinimumRaiseAmount => "Minimum raise amount must be greater than or equal to $0.5 so that there's enough liquidity for the launch", + + Self::FinalRaiseAmountAlreadySet => "The final raise amount has already been set", + + Self::TotalApprovedAmountTooLow => "Total approved amount must be greater than or equal to the minimum raise amount", + + Self::InvalidAdditionalTokensRecipient => "Invalid additional tokens recipient - should be set if additional tokens amount is greater than 0", + + Self::NoAdditionalTokensRecipientSet => "No additional tokens recipient set", + + Self::AdditionalTokensAlreadyClaimed => "Additional tokens already claimed", + + Self::FundingRecordApprovalPeriodOver => "Funding record approval period is over", + + Self::PerformancePackageAlreadyInitialized => "Performance package already initialized", + + Self::InvalidDao => "Invalid DAO", + + Self::InvalidAccumulatorActivationDelaySeconds => "Accumulator activation delay must be less than the launch duration", + + } + } + + /// Try to convert from error code + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::InvalidAmount), + + 6001 => Some(Self::SupplyNonZero), + + 6002 => Some(Self::InvalidSecondsForLaunch), + + 6003 => Some(Self::InsufficientFunds), + + 6004 => Some(Self::InvalidTokenKey), + + 6005 => Some(Self::InvalidLaunchState), + + 6006 => Some(Self::LaunchPeriodNotOver), + + 6007 => Some(Self::LaunchExpired), + + 6008 => Some(Self::LaunchNotRefunding), + + 6009 => Some(Self::LaunchNotInitialized), + + 6010 => Some(Self::FreezeAuthoritySet), + + 6011 => Some(Self::InvalidMonthlySpendingLimit), + + 6012 => Some(Self::InvalidMonthlySpendingLimitMembers), + + 6013 => Some(Self::InvalidPriceBasedPremineAmount), + + 6014 => Some(Self::InvalidPerformancePackageMinUnlockTime), + + 6015 => Some(Self::LaunchAuthorityNotSet), + + 6016 => Some(Self::FinalRaiseAmountTooLow), + + 6017 => Some(Self::TokensAlreadyClaimed), + + 6018 => Some(Self::MoneyAlreadyRefunded), + + 6019 => Some(Self::InvariantViolated), + + 6020 => Some(Self::LaunchNotLive), + + 6021 => Some(Self::InvalidMinimumRaiseAmount), + + 6022 => Some(Self::FinalRaiseAmountAlreadySet), + + 6023 => Some(Self::TotalApprovedAmountTooLow), + + 6024 => Some(Self::InvalidAdditionalTokensRecipient), + + 6025 => Some(Self::NoAdditionalTokensRecipientSet), + + 6026 => Some(Self::AdditionalTokensAlreadyClaimed), + + 6027 => Some(Self::FundingRecordApprovalPeriodOver), + + 6028 => Some(Self::PerformancePackageAlreadyInitialized), + + 6029 => Some(Self::InvalidDao), + + 6030 => Some(Self::InvalidAccumulatorActivationDelaySeconds), + + _ => None, + } + } + } + + impl std::fmt::Display for Launchpadv7Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.code(), self.msg()) + } + } + + impl std::error::Error for Launchpadv7Error {} + + // ------------------------------------------------------------------------ + // Composite Accounts + // ------------------------------------------------------------------------ + + /// Composite Account: staticAccounts + #[derive(Debug, Clone, Default)] + pub struct StaticAccountsInstructionAccountMetas { + pub futarchyProgram: AccountMeta, + + pub tokenMetadataProgram: AccountMeta, + + pub futarchyEventAuthority: AccountMeta, + + pub squadsProgram: AccountMeta, + + pub squadsProgramConfig: AccountMeta, + + pub squadsProgramConfigTreasury: AccountMeta, + + pub bidWallProgram: AccountMeta, + + pub bidWallEventAuthority: AccountMeta, + } + + impl StaticAccountsInstructionAccountMetas { + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.futarchyProgram.clone()); + + metas.push(self.tokenMetadataProgram.clone()); + + metas.push(self.futarchyEventAuthority.clone()); + + metas.push(self.squadsProgram.clone()); + + metas.push(self.squadsProgramConfig.clone()); + + metas.push(self.squadsProgramConfigTreasury.clone()); + + metas.push(self.bidWallProgram.clone()); + + metas.push(self.bidWallEventAuthority.clone()); + + metas + } + + fn set_from_accounts(&mut self, accounts: StaticAccountsInstructionAccounts) { + self.futarchyProgram = AccountMeta::new_readonly(accounts.futarchyProgram, false); + + self.tokenMetadataProgram = + AccountMeta::new_readonly(accounts.tokenMetadataProgram, false); + + self.futarchyEventAuthority = + AccountMeta::new_readonly(accounts.futarchyEventAuthority, false); + + self.squadsProgram = AccountMeta::new_readonly(accounts.squadsProgram, false); + + self.squadsProgramConfig = + AccountMeta::new_readonly(accounts.squadsProgramConfig, false); + + self.squadsProgramConfigTreasury = + AccountMeta::new(accounts.squadsProgramConfigTreasury, false); + + self.bidWallProgram = AccountMeta::new_readonly(accounts.bidWallProgram, false); + + self.bidWallEventAuthority = + AccountMeta::new_readonly(accounts.bidWallEventAuthority, false); + } + } + + #[derive(Debug, Clone)] + pub struct StaticAccountsInstructionAccounts { + pub futarchyProgram: Pubkey, + + pub tokenMetadataProgram: Pubkey, + + pub futarchyEventAuthority: Pubkey, + + pub squadsProgram: Pubkey, + + pub squadsProgramConfig: Pubkey, + + pub squadsProgramConfigTreasury: Pubkey, + + pub bidWallProgram: Pubkey, + + pub bidWallEventAuthority: Pubkey, + } + + impl StaticAccountsInstructionAccounts { + pub fn new( + futarchyProgram: Pubkey, + + tokenMetadataProgram: Pubkey, + + futarchyEventAuthority: Pubkey, + + squadsProgram: Pubkey, + + squadsProgramConfig: Pubkey, + + squadsProgramConfigTreasury: Pubkey, + + bidWallProgram: Pubkey, + + bidWallEventAuthority: Pubkey, + ) -> Self { + Self { + futarchyProgram, + + tokenMetadataProgram, + + futarchyEventAuthority, + + squadsProgram, + + squadsProgramConfig, + + squadsProgramConfigTreasury, + + bidWallProgram, + + bidWallEventAuthority, + } + } + } + + /// Composite Account: meteoraAccounts + #[derive(Debug, Clone, Default)] + pub struct MeteoraAccountsInstructionAccountMetas { + pub dammV2Program: AccountMeta, + + pub config: AccountMeta, + + pub token2022Program: AccountMeta, + + pub positionNftAccount: AccountMeta, + + pub pool: AccountMeta, + + pub position: AccountMeta, + + pub positionNftMint: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenAVault: AccountMeta, + + pub tokenBVault: AccountMeta, + + pub poolCreatorAuthority: AccountMeta, + + pub poolAuthority: AccountMeta, + + pub dammV2EventAuthority: AccountMeta, + } + + impl MeteoraAccountsInstructionAccountMetas { + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.dammV2Program.clone()); + + metas.push(self.config.clone()); + + metas.push(self.token2022Program.clone()); + + metas.push(self.positionNftAccount.clone()); + + metas.push(self.pool.clone()); + + metas.push(self.position.clone()); + + metas.push(self.positionNftMint.clone()); + + metas.push(self.baseMint.clone()); + + metas.push(self.quoteMint.clone()); + + metas.push(self.tokenAVault.clone()); + + metas.push(self.tokenBVault.clone()); + + metas.push(self.poolCreatorAuthority.clone()); + + metas.push(self.poolAuthority.clone()); + + metas.push(self.dammV2EventAuthority.clone()); + + metas + } + + fn set_from_accounts(&mut self, accounts: MeteoraAccountsInstructionAccounts) { + self.dammV2Program = AccountMeta::new_readonly(accounts.dammV2Program, false); + + self.config = AccountMeta::new_readonly(accounts.config, false); + + self.token2022Program = AccountMeta::new_readonly(accounts.token2022Program, false); + + self.positionNftAccount = AccountMeta::new(accounts.positionNftAccount, false); + + self.pool = AccountMeta::new(accounts.pool, false); + + self.position = AccountMeta::new(accounts.position, false); + + self.positionNftMint = AccountMeta::new(accounts.positionNftMint, false); + + self.baseMint = AccountMeta::new_readonly(accounts.baseMint, false); + + self.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.tokenAVault = AccountMeta::new(accounts.tokenAVault, false); + + self.tokenBVault = AccountMeta::new(accounts.tokenBVault, false); + + self.poolCreatorAuthority = + AccountMeta::new_readonly(accounts.poolCreatorAuthority, false); + + self.poolAuthority = AccountMeta::new_readonly(accounts.poolAuthority, false); + + self.dammV2EventAuthority = + AccountMeta::new_readonly(accounts.dammV2EventAuthority, false); + } + } + + #[derive(Debug, Clone)] + pub struct MeteoraAccountsInstructionAccounts { + pub dammV2Program: Pubkey, + + pub config: Pubkey, + + pub token2022Program: Pubkey, + + pub positionNftAccount: Pubkey, + + pub pool: Pubkey, + + pub position: Pubkey, + + pub positionNftMint: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenAVault: Pubkey, + + pub tokenBVault: Pubkey, + + pub poolCreatorAuthority: Pubkey, + + pub poolAuthority: Pubkey, + + pub dammV2EventAuthority: Pubkey, + } + + impl MeteoraAccountsInstructionAccounts { + pub fn new( + dammV2Program: Pubkey, + + config: Pubkey, + + token2022Program: Pubkey, + + positionNftAccount: Pubkey, + + pool: Pubkey, + + position: Pubkey, + + positionNftMint: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + tokenAVault: Pubkey, + + tokenBVault: Pubkey, + + poolCreatorAuthority: Pubkey, + + poolAuthority: Pubkey, + + dammV2EventAuthority: Pubkey, + ) -> Self { + Self { + dammV2Program, + + config, + + token2022Program, + + positionNftAccount, + + pool, + + position, + + positionNftMint, + + baseMint, + + quoteMint, + + tokenAVault, + + tokenBVault, + + poolCreatorAuthority, + + poolAuthority, + + dammV2EventAuthority, + } + } + } + + // ------------------------------------------------------------------------ + // Custom Types + // ------------------------------------------------------------------------ + + /// Custom struct: CommonFields + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct CommonFields { + pub slot: u64, + + pub unixTimestamp: i64, + + pub launchSeqNum: u64, + } + + impl CommonFields { + pub fn new(slot: u64, unixTimestamp: i64, launchSeqNum: u64) -> Self { + Self { + slot, + + unixTimestamp, + + launchSeqNum, + } + } + } + + /// Custom struct: InitializeLaunchArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitializeLaunchArgs { + pub minimumRaiseAmount: u64, + + pub monthlySpendingLimitAmount: u64, + + pub monthlySpendingLimitMembers: Vec, + + pub secondsForLaunch: u32, + + pub tokenName: String, + + pub tokenSymbol: String, + + pub tokenUri: String, + + pub performancePackageGrantee: Pubkey, + + pub performancePackageTokenAmount: u64, + + pub monthsUntilInsidersCanUnlock: u8, + + pub teamAddress: Pubkey, + + pub additionalTokensAmount: u64, + + pub accumulatorActivationDelaySeconds: u32, + } + + impl InitializeLaunchArgs { + pub fn new( + minimumRaiseAmount: u64, + + monthlySpendingLimitAmount: u64, + + monthlySpendingLimitMembers: Vec, + + secondsForLaunch: u32, + + tokenName: String, + + tokenSymbol: String, + + tokenUri: String, + + performancePackageGrantee: Pubkey, + + performancePackageTokenAmount: u64, + + monthsUntilInsidersCanUnlock: u8, + + teamAddress: Pubkey, + + additionalTokensAmount: u64, + + accumulatorActivationDelaySeconds: u32, + ) -> Self { + Self { + minimumRaiseAmount, + + monthlySpendingLimitAmount, + + monthlySpendingLimitMembers, + + secondsForLaunch, + + tokenName, + + tokenSymbol, + + tokenUri, + + performancePackageGrantee, + + performancePackageTokenAmount, + + monthsUntilInsidersCanUnlock, + + teamAddress, + + additionalTokensAmount, + + accumulatorActivationDelaySeconds, + } + } + } + + /// Custom enum: LaunchState + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum LaunchState { + Initialized, + + Live, + + Closed, + + Complete, + + Refunding, + } + + /// Custom struct: FundingRecord + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct FundingRecord { + pub pdaBump: u8, + + pub funder: Pubkey, + + pub launch: Pubkey, + + pub committedAmount: u64, + + pub isTokensClaimed: bool, + + pub isUsdcRefunded: bool, + + pub approvedAmount: u64, + + pub committedAmountAccumulator: u128, + + pub lastAccumulatorUpdate: i64, + } + + impl FundingRecord { + pub fn new( + pdaBump: u8, + + funder: Pubkey, + + launch: Pubkey, + + committedAmount: u64, + + isTokensClaimed: bool, + + isUsdcRefunded: bool, + + approvedAmount: u64, + + committedAmountAccumulator: u128, + + lastAccumulatorUpdate: i64, + ) -> Self { + Self { + pdaBump, + + funder, + + launch, + + committedAmount, + + isTokensClaimed, + + isUsdcRefunded, + + approvedAmount, + + committedAmountAccumulator, + + lastAccumulatorUpdate, + } + } + } + + /// Custom struct: OldFundingRecord + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct OldFundingRecord { + pub pdaBump: u8, + + pub funder: Pubkey, + + pub launch: Pubkey, + + pub committedAmount: u64, + + pub isTokensClaimed: bool, + + pub isUsdcRefunded: bool, + + pub approvedAmount: u64, + } + + impl OldFundingRecord { + pub fn new( + pdaBump: u8, + + funder: Pubkey, + + launch: Pubkey, + + committedAmount: u64, + + isTokensClaimed: bool, + + isUsdcRefunded: bool, + + approvedAmount: u64, + ) -> Self { + Self { + pdaBump, + + funder, + + launch, + + committedAmount, + + isTokensClaimed, + + isUsdcRefunded, + + approvedAmount, + } + } + } + + /// Custom struct: Launch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Launch { + pub pdaBump: u8, + + pub minimumRaiseAmount: u64, + + pub monthlySpendingLimitAmount: u64, + + pub monthlySpendingLimitMembers: Vec, + + pub launchAuthority: Pubkey, + + pub launchSigner: Pubkey, + + pub launchSignerPdaBump: u8, + + pub launchQuoteVault: Pubkey, + + pub launchBaseVault: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub unixTimestampStarted: Option, + + pub unixTimestampClosed: Option, + + pub totalCommittedAmount: u64, + + pub state: LaunchState, + + pub seqNum: u64, + + pub secondsForLaunch: u32, + + pub dao: Option, + + pub daoVault: Option, + + pub performancePackageGrantee: Pubkey, + + pub performancePackageTokenAmount: u64, + + pub monthsUntilInsidersCanUnlock: u8, + + pub teamAddress: Pubkey, + + pub totalApprovedAmount: u64, + + pub additionalTokensAmount: u64, + + pub additionalTokensRecipient: Option, + + pub additionalTokensClaimed: bool, + + pub unixTimestampCompleted: Option, + + pub isPerformancePackageInitialized: bool, + + pub accumulatorActivationDelaySeconds: u32, + } + + impl Launch { + pub fn new( + pdaBump: u8, + + minimumRaiseAmount: u64, + + monthlySpendingLimitAmount: u64, + + monthlySpendingLimitMembers: Vec, + + launchAuthority: Pubkey, + + launchSigner: Pubkey, + + launchSignerPdaBump: u8, + + launchQuoteVault: Pubkey, + + launchBaseVault: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + unixTimestampStarted: Option, + + unixTimestampClosed: Option, + + totalCommittedAmount: u64, + + state: LaunchState, + + seqNum: u64, + + secondsForLaunch: u32, + + dao: Option, + + daoVault: Option, + + performancePackageGrantee: Pubkey, + + performancePackageTokenAmount: u64, + + monthsUntilInsidersCanUnlock: u8, + + teamAddress: Pubkey, + + totalApprovedAmount: u64, + + additionalTokensAmount: u64, + + additionalTokensRecipient: Option, + + additionalTokensClaimed: bool, + + unixTimestampCompleted: Option, + + isPerformancePackageInitialized: bool, + + accumulatorActivationDelaySeconds: u32, + ) -> Self { + Self { + pdaBump, + + minimumRaiseAmount, + + monthlySpendingLimitAmount, + + monthlySpendingLimitMembers, + + launchAuthority, + + launchSigner, + + launchSignerPdaBump, + + launchQuoteVault, + + launchBaseVault, + + baseMint, + + quoteMint, + + unixTimestampStarted, + + unixTimestampClosed, + + totalCommittedAmount, + + state, + + seqNum, + + secondsForLaunch, + + dao, + + daoVault, + + performancePackageGrantee, + + performancePackageTokenAmount, + + monthsUntilInsidersCanUnlock, + + teamAddress, + + totalApprovedAmount, + + additionalTokensAmount, + + additionalTokensRecipient, + + additionalTokensClaimed, + + unixTimestampCompleted, + + isPerformancePackageInitialized, + + accumulatorActivationDelaySeconds, + } + } + } + + /// Custom struct: OldLaunch + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct OldLaunch { + pub pdaBump: u8, + + pub minimumRaiseAmount: u64, + + pub monthlySpendingLimitAmount: u64, + + pub monthlySpendingLimitMembers: Vec, + + pub launchAuthority: Pubkey, + + pub launchSigner: Pubkey, + + pub launchSignerPdaBump: u8, + + pub launchQuoteVault: Pubkey, + + pub launchBaseVault: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub unixTimestampStarted: Option, + + pub unixTimestampClosed: Option, + + pub totalCommittedAmount: u64, + + pub state: LaunchState, + + pub seqNum: u64, + + pub secondsForLaunch: u32, + + pub dao: Option, + + pub daoVault: Option, + + pub performancePackageGrantee: Pubkey, + + pub performancePackageTokenAmount: u64, + + pub monthsUntilInsidersCanUnlock: u8, + + pub teamAddress: Pubkey, + + pub totalApprovedAmount: u64, + + pub additionalTokensAmount: u64, + + pub additionalTokensRecipient: Option, + + pub additionalTokensClaimed: bool, + + pub unixTimestampCompleted: Option, + + pub isPerformancePackageInitialized: bool, + } + + impl OldLaunch { + pub fn new( + pdaBump: u8, + + minimumRaiseAmount: u64, + + monthlySpendingLimitAmount: u64, + + monthlySpendingLimitMembers: Vec, + + launchAuthority: Pubkey, + + launchSigner: Pubkey, + + launchSignerPdaBump: u8, + + launchQuoteVault: Pubkey, + + launchBaseVault: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + unixTimestampStarted: Option, + + unixTimestampClosed: Option, + + totalCommittedAmount: u64, + + state: LaunchState, + + seqNum: u64, + + secondsForLaunch: u32, + + dao: Option, + + daoVault: Option, + + performancePackageGrantee: Pubkey, + + performancePackageTokenAmount: u64, + + monthsUntilInsidersCanUnlock: u8, + + teamAddress: Pubkey, + + totalApprovedAmount: u64, + + additionalTokensAmount: u64, + + additionalTokensRecipient: Option, + + additionalTokensClaimed: bool, + + unixTimestampCompleted: Option, + + isPerformancePackageInitialized: bool, + ) -> Self { + Self { + pdaBump, + + minimumRaiseAmount, + + monthlySpendingLimitAmount, + + monthlySpendingLimitMembers, + + launchAuthority, + + launchSigner, + + launchSignerPdaBump, + + launchQuoteVault, + + launchBaseVault, + + baseMint, + + quoteMint, + + unixTimestampStarted, + + unixTimestampClosed, + + totalCommittedAmount, + + state, + + seqNum, + + secondsForLaunch, + + dao, + + daoVault, + + performancePackageGrantee, + + performancePackageTokenAmount, + + monthsUntilInsidersCanUnlock, + + teamAddress, + + totalApprovedAmount, + + additionalTokensAmount, + + additionalTokensRecipient, + + additionalTokensClaimed, + + unixTimestampCompleted, + + isPerformancePackageInitialized, + } + } + } +} + +// ---------------------------------------------------------------------------- +// Program: futarchy +// ---------------------------------------------------------------------------- +pub mod futarchy { + use super::*; + + // ------------------------------------------------------------------------ + // Program ID + // ------------------------------------------------------------------------ + + /// Returns the program ID for futarchy + pub fn program_id() -> Pubkey { + pubkey!("FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq") + } + + // ------------------------------------------------------------------------ + // Instructions + // ------------------------------------------------------------------------ + + // .................................................................... + // Instruction: InitializeDao + // .................................................................... + + /// Main instruction struct for InitializeDao + pub struct InitializeDaoInstruction { + pub accounts: InitializeDaoInstructionAccountMetas, + pub data: InitializeDaoInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeDao instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeDaoInstructionAccountMetas { + pub dao: AccountMeta, + + pub daoCreator: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigVault: AccountMeta, + + pub squadsProgram: AccountMeta, + + pub squadsProgramConfig: AccountMeta, + + pub squadsProgramConfigTreasury: AccountMeta, + + pub spendingLimit: AccountMeta, + + pub futarchyAmmBaseVault: AccountMeta, + + pub futarchyAmmQuoteVault: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeDao instruction + #[derive(Debug, Clone)] + pub struct InitializeDaoInstructionAccounts { + pub dao: Pubkey, + + pub daoCreator: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub squadsProgram: Pubkey, + + pub squadsProgramConfig: Pubkey, + + pub squadsProgramConfigTreasury: Pubkey, + + pub spendingLimit: Pubkey, + + pub futarchyAmmBaseVault: Pubkey, + + pub futarchyAmmQuoteVault: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeDaoInstructionAccounts { + pub fn new( + dao: Pubkey, + + daoCreator: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigVault: Pubkey, + + squadsProgram: Pubkey, + + squadsProgramConfig: Pubkey, + + squadsProgramConfigTreasury: Pubkey, + + spendingLimit: Pubkey, + + futarchyAmmBaseVault: Pubkey, + + futarchyAmmQuoteVault: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + daoCreator, + + payer, + + systemProgram, + + baseMint, + + quoteMint, + + squadsMultisig, + + squadsMultisigVault, + + squadsProgram, + + squadsProgramConfig, + + squadsProgramConfigTreasury, + + spendingLimit, + + futarchyAmmBaseVault, + + futarchyAmmQuoteVault, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeDao + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeDaoInstructionData { + pub params: InitializeDaoParams, + } + + impl InitializeDaoInstructionData { + pub fn new(params: InitializeDaoParams) -> Self { + Self { params } + } + } + + /// Implementation for InitializeDaoInstruction + impl InitializeDaoInstruction { + fn discriminator() -> [u8; 8] { + [128u8, 226u8, 96u8, 90u8, 39u8, 56u8, 24u8, 196u8] + } + + pub fn data(data: InitializeDaoInstructionData) -> Self { + Self { + accounts: InitializeDaoInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeDaoInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.daoCreator = AccountMeta::new_readonly(accounts.daoCreator, true); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.baseMint = AccountMeta::new_readonly(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.squadsMultisig = AccountMeta::new(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigVault = + AccountMeta::new_readonly(accounts.squadsMultisigVault, false); + + self.accounts.squadsProgram = AccountMeta::new_readonly(accounts.squadsProgram, false); + + self.accounts.squadsProgramConfig = + AccountMeta::new_readonly(accounts.squadsProgramConfig, false); + + self.accounts.squadsProgramConfigTreasury = + AccountMeta::new(accounts.squadsProgramConfigTreasury, false); + + self.accounts.spendingLimit = AccountMeta::new(accounts.spendingLimit, false); + + self.accounts.futarchyAmmBaseVault = + AccountMeta::new(accounts.futarchyAmmBaseVault, false); + + self.accounts.futarchyAmmQuoteVault = + AccountMeta::new(accounts.futarchyAmmQuoteVault, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.daoCreator.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigVault.clone()); + + metas.push(self.accounts.squadsProgram.clone()); + + metas.push(self.accounts.squadsProgramConfig.clone()); + + metas.push(self.accounts.squadsProgramConfigTreasury.clone()); + + metas.push(self.accounts.spendingLimit.clone()); + + metas.push(self.accounts.futarchyAmmBaseVault.clone()); + + metas.push(self.accounts.futarchyAmmQuoteVault.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: InitializeProposal + // .................................................................... + + /// Main instruction struct for InitializeProposal + pub struct InitializeProposalInstruction { + pub accounts: InitializeProposalInstructionAccountMetas, + pub data: InitializeProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeProposal instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub squadsProposal: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub dao: AccountMeta, + + pub question: AccountMeta, + + pub quoteVault: AccountMeta, + + pub baseVault: AccountMeta, + + pub proposer: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeProposal instruction + #[derive(Debug, Clone)] + pub struct InitializeProposalInstructionAccounts { + pub proposal: Pubkey, + + pub squadsProposal: Pubkey, + + pub squadsMultisig: Pubkey, + + pub dao: Pubkey, + + pub question: Pubkey, + + pub quoteVault: Pubkey, + + pub baseVault: Pubkey, + + pub proposer: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + squadsProposal: Pubkey, + + squadsMultisig: Pubkey, + + dao: Pubkey, + + question: Pubkey, + + quoteVault: Pubkey, + + baseVault: Pubkey, + + proposer: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + squadsProposal, + + squadsMultisig, + + dao, + + question, + + quoteVault, + + baseVault, + + proposer, + + payer, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeProposalInstructionData {} + + impl InitializeProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for InitializeProposalInstruction + impl InitializeProposalInstruction { + fn discriminator() -> [u8; 8] { + [50u8, 73u8, 156u8, 98u8, 129u8, 149u8, 21u8, 158u8] + } + + pub fn data(data: InitializeProposalInstructionData) -> Self { + Self { + accounts: InitializeProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.squadsProposal = + AccountMeta::new_readonly(accounts.squadsProposal, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.quoteVault = AccountMeta::new_readonly(accounts.quoteVault, false); + + self.accounts.baseVault = AccountMeta::new_readonly(accounts.baseVault, false); + + self.accounts.proposer = AccountMeta::new_readonly(accounts.proposer, true); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.squadsProposal.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.proposer.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: StakeToProposal + // .................................................................... + + /// Main instruction struct for StakeToProposal + pub struct StakeToProposalInstruction { + pub accounts: StakeToProposalInstructionAccountMetas, + pub data: StakeToProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for StakeToProposal instruction + #[derive(Debug, Clone, Default)] + pub struct StakeToProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub stakerBaseAccount: AccountMeta, + + pub proposalBaseAccount: AccountMeta, + + pub stakeAccount: AccountMeta, + + pub staker: AccountMeta, + + pub payer: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for StakeToProposal instruction + #[derive(Debug, Clone)] + pub struct StakeToProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub stakerBaseAccount: Pubkey, + + pub proposalBaseAccount: Pubkey, + + pub stakeAccount: Pubkey, + + pub staker: Pubkey, + + pub payer: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl StakeToProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + stakerBaseAccount: Pubkey, + + proposalBaseAccount: Pubkey, + + stakeAccount: Pubkey, + + staker: Pubkey, + + payer: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + stakerBaseAccount, + + proposalBaseAccount, + + stakeAccount, + + staker, + + payer, + + tokenProgram, + + associatedTokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for StakeToProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct StakeToProposalInstructionData { + pub params: StakeToProposalParams, + } + + impl StakeToProposalInstructionData { + pub fn new(params: StakeToProposalParams) -> Self { + Self { params } + } + } + + /// Implementation for StakeToProposalInstruction + impl StakeToProposalInstruction { + fn discriminator() -> [u8; 8] { + [10u8, 169u8, 175u8, 238u8, 80u8, 221u8, 37u8, 16u8] + } + + pub fn data(data: StakeToProposalInstructionData) -> Self { + Self { + accounts: StakeToProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: StakeToProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.stakerBaseAccount = AccountMeta::new(accounts.stakerBaseAccount, false); + + self.accounts.proposalBaseAccount = + AccountMeta::new(accounts.proposalBaseAccount, false); + + self.accounts.stakeAccount = AccountMeta::new(accounts.stakeAccount, false); + + self.accounts.staker = AccountMeta::new_readonly(accounts.staker, true); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.stakerBaseAccount.clone()); + + metas.push(self.accounts.proposalBaseAccount.clone()); + + metas.push(self.accounts.stakeAccount.clone()); + + metas.push(self.accounts.staker.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: UnstakeFromProposal + // .................................................................... + + /// Main instruction struct for UnstakeFromProposal + pub struct UnstakeFromProposalInstruction { + pub accounts: UnstakeFromProposalInstructionAccountMetas, + pub data: UnstakeFromProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for UnstakeFromProposal instruction + #[derive(Debug, Clone, Default)] + pub struct UnstakeFromProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub stakerBaseAccount: AccountMeta, + + pub proposalBaseAccount: AccountMeta, + + pub stakeAccount: AccountMeta, + + pub staker: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for UnstakeFromProposal instruction + #[derive(Debug, Clone)] + pub struct UnstakeFromProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub stakerBaseAccount: Pubkey, + + pub proposalBaseAccount: Pubkey, + + pub stakeAccount: Pubkey, + + pub staker: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl UnstakeFromProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + stakerBaseAccount: Pubkey, + + proposalBaseAccount: Pubkey, + + stakeAccount: Pubkey, + + staker: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + stakerBaseAccount, + + proposalBaseAccount, + + stakeAccount, + + staker, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for UnstakeFromProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct UnstakeFromProposalInstructionData { + pub params: UnstakeFromProposalParams, + } + + impl UnstakeFromProposalInstructionData { + pub fn new(params: UnstakeFromProposalParams) -> Self { + Self { params } + } + } + + /// Implementation for UnstakeFromProposalInstruction + impl UnstakeFromProposalInstruction { + fn discriminator() -> [u8; 8] { + [179u8, 220u8, 186u8, 86u8, 2u8, 96u8, 50u8, 161u8] + } + + pub fn data(data: UnstakeFromProposalInstructionData) -> Self { + Self { + accounts: UnstakeFromProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: UnstakeFromProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.stakerBaseAccount = AccountMeta::new(accounts.stakerBaseAccount, false); + + self.accounts.proposalBaseAccount = + AccountMeta::new(accounts.proposalBaseAccount, false); + + self.accounts.stakeAccount = AccountMeta::new(accounts.stakeAccount, false); + + self.accounts.staker = AccountMeta::new_readonly(accounts.staker, true); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.stakerBaseAccount.clone()); + + metas.push(self.accounts.proposalBaseAccount.clone()); + + metas.push(self.accounts.stakeAccount.clone()); + + metas.push(self.accounts.staker.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: LaunchProposal + // .................................................................... + + /// Main instruction struct for LaunchProposal + pub struct LaunchProposalInstruction { + pub accounts: LaunchProposalInstructionAccountMetas, + pub data: LaunchProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for LaunchProposal instruction + #[derive(Debug, Clone, Default)] + pub struct LaunchProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub baseVault: AccountMeta, + + pub quoteVault: AccountMeta, + + pub passBaseMint: AccountMeta, + + pub passQuoteMint: AccountMeta, + + pub failBaseMint: AccountMeta, + + pub failQuoteMint: AccountMeta, + + pub dao: AccountMeta, + + pub payer: AccountMeta, + + pub ammPassBaseVault: AccountMeta, + + pub ammPassQuoteVault: AccountMeta, + + pub ammFailBaseVault: AccountMeta, + + pub ammFailQuoteVault: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsProposal: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for LaunchProposal instruction + #[derive(Debug, Clone)] + pub struct LaunchProposalInstructionAccounts { + pub proposal: Pubkey, + + pub baseVault: Pubkey, + + pub quoteVault: Pubkey, + + pub passBaseMint: Pubkey, + + pub passQuoteMint: Pubkey, + + pub failBaseMint: Pubkey, + + pub failQuoteMint: Pubkey, + + pub dao: Pubkey, + + pub payer: Pubkey, + + pub ammPassBaseVault: Pubkey, + + pub ammPassQuoteVault: Pubkey, + + pub ammFailBaseVault: Pubkey, + + pub ammFailQuoteVault: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsProposal: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl LaunchProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + baseVault: Pubkey, + + quoteVault: Pubkey, + + passBaseMint: Pubkey, + + passQuoteMint: Pubkey, + + failBaseMint: Pubkey, + + failQuoteMint: Pubkey, + + dao: Pubkey, + + payer: Pubkey, + + ammPassBaseVault: Pubkey, + + ammPassQuoteVault: Pubkey, + + ammFailBaseVault: Pubkey, + + ammFailQuoteVault: Pubkey, + + squadsMultisig: Pubkey, + + squadsProposal: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + baseVault, + + quoteVault, + + passBaseMint, + + passQuoteMint, + + failBaseMint, + + failQuoteMint, + + dao, + + payer, + + ammPassBaseVault, + + ammPassQuoteVault, + + ammFailBaseVault, + + ammFailQuoteVault, + + squadsMultisig, + + squadsProposal, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for LaunchProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct LaunchProposalInstructionData {} + + impl LaunchProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for LaunchProposalInstruction + impl LaunchProposalInstruction { + fn discriminator() -> [u8; 8] { + [16u8, 211u8, 189u8, 119u8, 245u8, 72u8, 0u8, 229u8] + } + + pub fn data(data: LaunchProposalInstructionData) -> Self { + Self { + accounts: LaunchProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: LaunchProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.baseVault = AccountMeta::new_readonly(accounts.baseVault, false); + + self.accounts.quoteVault = AccountMeta::new_readonly(accounts.quoteVault, false); + + self.accounts.passBaseMint = AccountMeta::new_readonly(accounts.passBaseMint, false); + + self.accounts.passQuoteMint = AccountMeta::new_readonly(accounts.passQuoteMint, false); + + self.accounts.failBaseMint = AccountMeta::new_readonly(accounts.failBaseMint, false); + + self.accounts.failQuoteMint = AccountMeta::new_readonly(accounts.failQuoteMint, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.ammPassBaseVault = AccountMeta::new(accounts.ammPassBaseVault, false); + + self.accounts.ammPassQuoteVault = AccountMeta::new(accounts.ammPassQuoteVault, false); + + self.accounts.ammFailBaseVault = AccountMeta::new(accounts.ammFailBaseVault, false); + + self.accounts.ammFailQuoteVault = AccountMeta::new(accounts.ammFailQuoteVault, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.squadsProposal = + AccountMeta::new_readonly(accounts.squadsProposal, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.passBaseMint.clone()); + + metas.push(self.accounts.passQuoteMint.clone()); + + metas.push(self.accounts.failBaseMint.clone()); + + metas.push(self.accounts.failQuoteMint.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.ammPassBaseVault.clone()); + + metas.push(self.accounts.ammPassQuoteVault.clone()); + + metas.push(self.accounts.ammFailBaseVault.clone()); + + metas.push(self.accounts.ammFailQuoteVault.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsProposal.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: FinalizeProposal + // .................................................................... + + /// Main instruction struct for FinalizeProposal + pub struct FinalizeProposalInstruction { + pub accounts: FinalizeProposalInstructionAccountMetas, + pub data: FinalizeProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for FinalizeProposal instruction + #[derive(Debug, Clone, Default)] + pub struct FinalizeProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub question: AccountMeta, + + pub squadsProposal: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigProgram: AccountMeta, + + pub ammPassBaseVault: AccountMeta, + + pub ammPassQuoteVault: AccountMeta, + + pub ammFailBaseVault: AccountMeta, + + pub ammFailQuoteVault: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub vaultProgram: AccountMeta, + + pub vaultEventAuthority: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub quoteVault: AccountMeta, + + pub quoteVaultUnderlyingTokenAccount: AccountMeta, + + pub passQuoteMint: AccountMeta, + + pub failQuoteMint: AccountMeta, + + pub passBaseMint: AccountMeta, + + pub failBaseMint: AccountMeta, + + pub baseVault: AccountMeta, + + pub baseVaultUnderlyingTokenAccount: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for FinalizeProposal instruction + #[derive(Debug, Clone)] + pub struct FinalizeProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub question: Pubkey, + + pub squadsProposal: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigProgram: Pubkey, + + pub ammPassBaseVault: Pubkey, + + pub ammPassQuoteVault: Pubkey, + + pub ammFailBaseVault: Pubkey, + + pub ammFailQuoteVault: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub vaultProgram: Pubkey, + + pub vaultEventAuthority: Pubkey, + + pub tokenProgram: Pubkey, + + pub quoteVault: Pubkey, + + pub quoteVaultUnderlyingTokenAccount: Pubkey, + + pub passQuoteMint: Pubkey, + + pub failQuoteMint: Pubkey, + + pub passBaseMint: Pubkey, + + pub failBaseMint: Pubkey, + + pub baseVault: Pubkey, + + pub baseVaultUnderlyingTokenAccount: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl FinalizeProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + question: Pubkey, + + squadsProposal: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigProgram: Pubkey, + + ammPassBaseVault: Pubkey, + + ammPassQuoteVault: Pubkey, + + ammFailBaseVault: Pubkey, + + ammFailQuoteVault: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + vaultProgram: Pubkey, + + vaultEventAuthority: Pubkey, + + tokenProgram: Pubkey, + + quoteVault: Pubkey, + + quoteVaultUnderlyingTokenAccount: Pubkey, + + passQuoteMint: Pubkey, + + failQuoteMint: Pubkey, + + passBaseMint: Pubkey, + + failBaseMint: Pubkey, + + baseVault: Pubkey, + + baseVaultUnderlyingTokenAccount: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + question, + + squadsProposal, + + squadsMultisig, + + squadsMultisigProgram, + + ammPassBaseVault, + + ammPassQuoteVault, + + ammFailBaseVault, + + ammFailQuoteVault, + + ammBaseVault, + + ammQuoteVault, + + vaultProgram, + + vaultEventAuthority, + + tokenProgram, + + quoteVault, + + quoteVaultUnderlyingTokenAccount, + + passQuoteMint, + + failQuoteMint, + + passBaseMint, + + failBaseMint, + + baseVault, + + baseVaultUnderlyingTokenAccount, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for FinalizeProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct FinalizeProposalInstructionData {} + + impl FinalizeProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for FinalizeProposalInstruction + impl FinalizeProposalInstruction { + fn discriminator() -> [u8; 8] { + [23u8, 68u8, 51u8, 167u8, 109u8, 173u8, 187u8, 164u8] + } + + pub fn data(data: FinalizeProposalInstructionData) -> Self { + Self { + accounts: FinalizeProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: FinalizeProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.question = AccountMeta::new(accounts.question, false); + + self.accounts.squadsProposal = AccountMeta::new(accounts.squadsProposal, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigProgram = + AccountMeta::new_readonly(accounts.squadsMultisigProgram, false); + + self.accounts.ammPassBaseVault = AccountMeta::new(accounts.ammPassBaseVault, false); + + self.accounts.ammPassQuoteVault = AccountMeta::new(accounts.ammPassQuoteVault, false); + + self.accounts.ammFailBaseVault = AccountMeta::new(accounts.ammFailBaseVault, false); + + self.accounts.ammFailQuoteVault = AccountMeta::new(accounts.ammFailQuoteVault, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.vaultProgram = AccountMeta::new_readonly(accounts.vaultProgram, false); + + self.accounts.vaultEventAuthority = + AccountMeta::new_readonly(accounts.vaultEventAuthority, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.quoteVault = AccountMeta::new(accounts.quoteVault, false); + + self.accounts.quoteVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.quoteVaultUnderlyingTokenAccount, false); + + self.accounts.passQuoteMint = AccountMeta::new(accounts.passQuoteMint, false); + + self.accounts.failQuoteMint = AccountMeta::new(accounts.failQuoteMint, false); + + self.accounts.passBaseMint = AccountMeta::new(accounts.passBaseMint, false); + + self.accounts.failBaseMint = AccountMeta::new(accounts.failBaseMint, false); + + self.accounts.baseVault = AccountMeta::new(accounts.baseVault, false); + + self.accounts.baseVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.baseVaultUnderlyingTokenAccount, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.squadsProposal.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigProgram.clone()); + + metas.push(self.accounts.ammPassBaseVault.clone()); + + metas.push(self.accounts.ammPassQuoteVault.clone()); + + metas.push(self.accounts.ammFailBaseVault.clone()); + + metas.push(self.accounts.ammFailQuoteVault.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.vaultProgram.clone()); + + metas.push(self.accounts.vaultEventAuthority.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.quoteVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.passQuoteMint.clone()); + + metas.push(self.accounts.failQuoteMint.clone()); + + metas.push(self.accounts.passBaseMint.clone()); + + metas.push(self.accounts.failBaseMint.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.baseVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: UpdateDao + // .................................................................... + + /// Main instruction struct for UpdateDao + pub struct UpdateDaoInstruction { + pub accounts: UpdateDaoInstructionAccountMetas, + pub data: UpdateDaoInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for UpdateDao instruction + #[derive(Debug, Clone, Default)] + pub struct UpdateDaoInstructionAccountMetas { + pub dao: AccountMeta, + + pub squadsMultisigVault: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for UpdateDao instruction + #[derive(Debug, Clone)] + pub struct UpdateDaoInstructionAccounts { + pub dao: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl UpdateDaoInstructionAccounts { + pub fn new( + dao: Pubkey, + + squadsMultisigVault: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + squadsMultisigVault, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for UpdateDao + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct UpdateDaoInstructionData { + pub daoParams: UpdateDaoParams, + } + + impl UpdateDaoInstructionData { + pub fn new(daoParams: UpdateDaoParams) -> Self { + Self { daoParams } + } + } + + /// Implementation for UpdateDaoInstruction + impl UpdateDaoInstruction { + fn discriminator() -> [u8; 8] { + [131u8, 72u8, 75u8, 25u8, 112u8, 210u8, 109u8, 2u8] + } + + pub fn data(data: UpdateDaoInstructionData) -> Self { + Self { + accounts: UpdateDaoInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: UpdateDaoInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.squadsMultisigVault = + AccountMeta::new_readonly(accounts.squadsMultisigVault, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.squadsMultisigVault.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: SpotSwap + // .................................................................... + + /// Main instruction struct for SpotSwap + pub struct SpotSwapInstruction { + pub accounts: SpotSwapInstructionAccountMetas, + pub data: SpotSwapInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for SpotSwap instruction + #[derive(Debug, Clone, Default)] + pub struct SpotSwapInstructionAccountMetas { + pub dao: AccountMeta, + + pub userBaseAccount: AccountMeta, + + pub userQuoteAccount: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub user: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for SpotSwap instruction + #[derive(Debug, Clone)] + pub struct SpotSwapInstructionAccounts { + pub dao: Pubkey, + + pub userBaseAccount: Pubkey, + + pub userQuoteAccount: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub user: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl SpotSwapInstructionAccounts { + pub fn new( + dao: Pubkey, + + userBaseAccount: Pubkey, + + userQuoteAccount: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + user: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + userBaseAccount, + + userQuoteAccount, + + ammBaseVault, + + ammQuoteVault, + + user, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for SpotSwap + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct SpotSwapInstructionData { + pub params: SpotSwapParams, + } + + impl SpotSwapInstructionData { + pub fn new(params: SpotSwapParams) -> Self { + Self { params } + } + } + + /// Implementation for SpotSwapInstruction + impl SpotSwapInstruction { + fn discriminator() -> [u8; 8] { + [167u8, 97u8, 12u8, 231u8, 237u8, 78u8, 166u8, 251u8] + } + + pub fn data(data: SpotSwapInstructionData) -> Self { + Self { + accounts: SpotSwapInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: SpotSwapInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.userBaseAccount = AccountMeta::new(accounts.userBaseAccount, false); + + self.accounts.userQuoteAccount = AccountMeta::new(accounts.userQuoteAccount, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.user = AccountMeta::new_readonly(accounts.user, true); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.userBaseAccount.clone()); + + metas.push(self.accounts.userQuoteAccount.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.user.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ConditionalSwap + // .................................................................... + + /// Main instruction struct for ConditionalSwap + pub struct ConditionalSwapInstruction { + pub accounts: ConditionalSwapInstructionAccountMetas, + pub data: ConditionalSwapInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ConditionalSwap instruction + #[derive(Debug, Clone, Default)] + pub struct ConditionalSwapInstructionAccountMetas { + pub dao: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub proposal: AccountMeta, + + pub ammPassBaseVault: AccountMeta, + + pub ammPassQuoteVault: AccountMeta, + + pub ammFailBaseVault: AccountMeta, + + pub ammFailQuoteVault: AccountMeta, + + pub trader: AccountMeta, + + pub userInputAccount: AccountMeta, + + pub userOutputAccount: AccountMeta, + + pub baseVault: AccountMeta, + + pub baseVaultUnderlyingTokenAccount: AccountMeta, + + pub quoteVault: AccountMeta, + + pub quoteVaultUnderlyingTokenAccount: AccountMeta, + + pub passBaseMint: AccountMeta, + + pub failBaseMint: AccountMeta, + + pub passQuoteMint: AccountMeta, + + pub failQuoteMint: AccountMeta, + + pub conditionalVaultProgram: AccountMeta, + + pub vaultEventAuthority: AccountMeta, + + pub question: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ConditionalSwap instruction + #[derive(Debug, Clone)] + pub struct ConditionalSwapInstructionAccounts { + pub dao: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub proposal: Pubkey, + + pub ammPassBaseVault: Pubkey, + + pub ammPassQuoteVault: Pubkey, + + pub ammFailBaseVault: Pubkey, + + pub ammFailQuoteVault: Pubkey, + + pub trader: Pubkey, + + pub userInputAccount: Pubkey, + + pub userOutputAccount: Pubkey, + + pub baseVault: Pubkey, + + pub baseVaultUnderlyingTokenAccount: Pubkey, + + pub quoteVault: Pubkey, + + pub quoteVaultUnderlyingTokenAccount: Pubkey, + + pub passBaseMint: Pubkey, + + pub failBaseMint: Pubkey, + + pub passQuoteMint: Pubkey, + + pub failQuoteMint: Pubkey, + + pub conditionalVaultProgram: Pubkey, + + pub vaultEventAuthority: Pubkey, + + pub question: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ConditionalSwapInstructionAccounts { + pub fn new( + dao: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + proposal: Pubkey, + + ammPassBaseVault: Pubkey, + + ammPassQuoteVault: Pubkey, + + ammFailBaseVault: Pubkey, + + ammFailQuoteVault: Pubkey, + + trader: Pubkey, + + userInputAccount: Pubkey, + + userOutputAccount: Pubkey, + + baseVault: Pubkey, + + baseVaultUnderlyingTokenAccount: Pubkey, + + quoteVault: Pubkey, + + quoteVaultUnderlyingTokenAccount: Pubkey, + + passBaseMint: Pubkey, + + failBaseMint: Pubkey, + + passQuoteMint: Pubkey, + + failQuoteMint: Pubkey, + + conditionalVaultProgram: Pubkey, + + vaultEventAuthority: Pubkey, + + question: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + ammBaseVault, + + ammQuoteVault, + + proposal, + + ammPassBaseVault, + + ammPassQuoteVault, + + ammFailBaseVault, + + ammFailQuoteVault, + + trader, + + userInputAccount, + + userOutputAccount, + + baseVault, + + baseVaultUnderlyingTokenAccount, + + quoteVault, + + quoteVaultUnderlyingTokenAccount, + + passBaseMint, + + failBaseMint, + + passQuoteMint, + + failQuoteMint, + + conditionalVaultProgram, + + vaultEventAuthority, + + question, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ConditionalSwap + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ConditionalSwapInstructionData { + pub params: ConditionalSwapParams, + } + + impl ConditionalSwapInstructionData { + pub fn new(params: ConditionalSwapParams) -> Self { + Self { params } + } + } + + /// Implementation for ConditionalSwapInstruction + impl ConditionalSwapInstruction { + fn discriminator() -> [u8; 8] { + [194u8, 136u8, 220u8, 89u8, 242u8, 169u8, 130u8, 157u8] + } + + pub fn data(data: ConditionalSwapInstructionData) -> Self { + Self { + accounts: ConditionalSwapInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ConditionalSwapInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.proposal = AccountMeta::new_readonly(accounts.proposal, false); + + self.accounts.ammPassBaseVault = AccountMeta::new(accounts.ammPassBaseVault, false); + + self.accounts.ammPassQuoteVault = AccountMeta::new(accounts.ammPassQuoteVault, false); + + self.accounts.ammFailBaseVault = AccountMeta::new(accounts.ammFailBaseVault, false); + + self.accounts.ammFailQuoteVault = AccountMeta::new(accounts.ammFailQuoteVault, false); + + self.accounts.trader = AccountMeta::new_readonly(accounts.trader, true); + + self.accounts.userInputAccount = AccountMeta::new(accounts.userInputAccount, false); + + self.accounts.userOutputAccount = AccountMeta::new(accounts.userOutputAccount, false); + + self.accounts.baseVault = AccountMeta::new(accounts.baseVault, false); + + self.accounts.baseVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.baseVaultUnderlyingTokenAccount, false); + + self.accounts.quoteVault = AccountMeta::new(accounts.quoteVault, false); + + self.accounts.quoteVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.quoteVaultUnderlyingTokenAccount, false); + + self.accounts.passBaseMint = AccountMeta::new(accounts.passBaseMint, false); + + self.accounts.failBaseMint = AccountMeta::new(accounts.failBaseMint, false); + + self.accounts.passQuoteMint = AccountMeta::new(accounts.passQuoteMint, false); + + self.accounts.failQuoteMint = AccountMeta::new(accounts.failQuoteMint, false); + + self.accounts.conditionalVaultProgram = + AccountMeta::new_readonly(accounts.conditionalVaultProgram, false); + + self.accounts.vaultEventAuthority = + AccountMeta::new_readonly(accounts.vaultEventAuthority, false); + + self.accounts.question = AccountMeta::new_readonly(accounts.question, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.ammPassBaseVault.clone()); + + metas.push(self.accounts.ammPassQuoteVault.clone()); + + metas.push(self.accounts.ammFailBaseVault.clone()); + + metas.push(self.accounts.ammFailQuoteVault.clone()); + + metas.push(self.accounts.trader.clone()); + + metas.push(self.accounts.userInputAccount.clone()); + + metas.push(self.accounts.userOutputAccount.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.baseVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.quoteVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.passBaseMint.clone()); + + metas.push(self.accounts.failBaseMint.clone()); + + metas.push(self.accounts.passQuoteMint.clone()); + + metas.push(self.accounts.failQuoteMint.clone()); + + metas.push(self.accounts.conditionalVaultProgram.clone()); + + metas.push(self.accounts.vaultEventAuthority.clone()); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ProvideLiquidity + // .................................................................... + + /// Main instruction struct for ProvideLiquidity + pub struct ProvideLiquidityInstruction { + pub accounts: ProvideLiquidityInstructionAccountMetas, + pub data: ProvideLiquidityInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ProvideLiquidity instruction + #[derive(Debug, Clone, Default)] + pub struct ProvideLiquidityInstructionAccountMetas { + pub dao: AccountMeta, + + pub liquidityProvider: AccountMeta, + + pub liquidityProviderBaseAccount: AccountMeta, + + pub liquidityProviderQuoteAccount: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub ammPosition: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ProvideLiquidity instruction + #[derive(Debug, Clone)] + pub struct ProvideLiquidityInstructionAccounts { + pub dao: Pubkey, + + pub liquidityProvider: Pubkey, + + pub liquidityProviderBaseAccount: Pubkey, + + pub liquidityProviderQuoteAccount: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub ammPosition: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ProvideLiquidityInstructionAccounts { + pub fn new( + dao: Pubkey, + + liquidityProvider: Pubkey, + + liquidityProviderBaseAccount: Pubkey, + + liquidityProviderQuoteAccount: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + ammPosition: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + liquidityProvider, + + liquidityProviderBaseAccount, + + liquidityProviderQuoteAccount, + + payer, + + systemProgram, + + ammBaseVault, + + ammQuoteVault, + + ammPosition, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ProvideLiquidity + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ProvideLiquidityInstructionData { + pub params: ProvideLiquidityParams, + } + + impl ProvideLiquidityInstructionData { + pub fn new(params: ProvideLiquidityParams) -> Self { + Self { params } + } + } + + /// Implementation for ProvideLiquidityInstruction + impl ProvideLiquidityInstruction { + fn discriminator() -> [u8; 8] { + [40u8, 110u8, 107u8, 116u8, 174u8, 127u8, 97u8, 204u8] + } + + pub fn data(data: ProvideLiquidityInstructionData) -> Self { + Self { + accounts: ProvideLiquidityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ProvideLiquidityInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.liquidityProvider = + AccountMeta::new_readonly(accounts.liquidityProvider, true); + + self.accounts.liquidityProviderBaseAccount = + AccountMeta::new(accounts.liquidityProviderBaseAccount, false); + + self.accounts.liquidityProviderQuoteAccount = + AccountMeta::new(accounts.liquidityProviderQuoteAccount, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.ammPosition = AccountMeta::new(accounts.ammPosition, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.liquidityProvider.clone()); + + metas.push(self.accounts.liquidityProviderBaseAccount.clone()); + + metas.push(self.accounts.liquidityProviderQuoteAccount.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.ammPosition.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: WithdrawLiquidity + // .................................................................... + + /// Main instruction struct for WithdrawLiquidity + pub struct WithdrawLiquidityInstruction { + pub accounts: WithdrawLiquidityInstructionAccountMetas, + pub data: WithdrawLiquidityInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for WithdrawLiquidity instruction + #[derive(Debug, Clone, Default)] + pub struct WithdrawLiquidityInstructionAccountMetas { + pub dao: AccountMeta, + + pub positionAuthority: AccountMeta, + + pub liquidityProviderBaseAccount: AccountMeta, + + pub liquidityProviderQuoteAccount: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub ammPosition: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for WithdrawLiquidity instruction + #[derive(Debug, Clone)] + pub struct WithdrawLiquidityInstructionAccounts { + pub dao: Pubkey, + + pub positionAuthority: Pubkey, + + pub liquidityProviderBaseAccount: Pubkey, + + pub liquidityProviderQuoteAccount: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub ammPosition: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl WithdrawLiquidityInstructionAccounts { + pub fn new( + dao: Pubkey, + + positionAuthority: Pubkey, + + liquidityProviderBaseAccount: Pubkey, + + liquidityProviderQuoteAccount: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + ammPosition: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + positionAuthority, + + liquidityProviderBaseAccount, + + liquidityProviderQuoteAccount, + + ammBaseVault, + + ammQuoteVault, + + ammPosition, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for WithdrawLiquidity + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct WithdrawLiquidityInstructionData { + pub params: WithdrawLiquidityParams, + } + + impl WithdrawLiquidityInstructionData { + pub fn new(params: WithdrawLiquidityParams) -> Self { + Self { params } + } + } + + /// Implementation for WithdrawLiquidityInstruction + impl WithdrawLiquidityInstruction { + fn discriminator() -> [u8; 8] { + [149u8, 158u8, 33u8, 185u8, 47u8, 243u8, 253u8, 31u8] + } + + pub fn data(data: WithdrawLiquidityInstructionData) -> Self { + Self { + accounts: WithdrawLiquidityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: WithdrawLiquidityInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.positionAuthority = + AccountMeta::new_readonly(accounts.positionAuthority, true); + + self.accounts.liquidityProviderBaseAccount = + AccountMeta::new(accounts.liquidityProviderBaseAccount, false); + + self.accounts.liquidityProviderQuoteAccount = + AccountMeta::new(accounts.liquidityProviderQuoteAccount, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.ammPosition = AccountMeta::new(accounts.ammPosition, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.positionAuthority.clone()); + + metas.push(self.accounts.liquidityProviderBaseAccount.clone()); + + metas.push(self.accounts.liquidityProviderQuoteAccount.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.ammPosition.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CollectFees + // .................................................................... + + /// Main instruction struct for CollectFees + pub struct CollectFeesInstruction { + pub accounts: CollectFeesInstructionAccountMetas, + pub data: CollectFeesInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CollectFees instruction + #[derive(Debug, Clone, Default)] + pub struct CollectFeesInstructionAccountMetas { + pub dao: AccountMeta, + + pub admin: AccountMeta, + + pub baseTokenAccount: AccountMeta, + + pub quoteTokenAccount: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CollectFees instruction + #[derive(Debug, Clone)] + pub struct CollectFeesInstructionAccounts { + pub dao: Pubkey, + + pub admin: Pubkey, + + pub baseTokenAccount: Pubkey, + + pub quoteTokenAccount: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CollectFeesInstructionAccounts { + pub fn new( + dao: Pubkey, + + admin: Pubkey, + + baseTokenAccount: Pubkey, + + quoteTokenAccount: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + admin, + + baseTokenAccount, + + quoteTokenAccount, + + ammBaseVault, + + ammQuoteVault, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CollectFees + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CollectFeesInstructionData {} + + impl CollectFeesInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CollectFeesInstruction + impl CollectFeesInstruction { + fn discriminator() -> [u8; 8] { + [164u8, 152u8, 207u8, 99u8, 30u8, 186u8, 19u8, 182u8] + } + + pub fn data(data: CollectFeesInstructionData) -> Self { + Self { + accounts: CollectFeesInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CollectFeesInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + + self.accounts.baseTokenAccount = AccountMeta::new(accounts.baseTokenAccount, false); + + self.accounts.quoteTokenAccount = AccountMeta::new(accounts.quoteTokenAccount, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.baseTokenAccount.clone()); + + metas.push(self.accounts.quoteTokenAccount.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ExecuteSpendingLimitChange + // .................................................................... + + /// Main instruction struct for ExecuteSpendingLimitChange + pub struct ExecuteSpendingLimitChangeInstruction { + pub accounts: ExecuteSpendingLimitChangeInstructionAccountMetas, + pub data: ExecuteSpendingLimitChangeInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ExecuteSpendingLimitChange instruction + #[derive(Debug, Clone, Default)] + pub struct ExecuteSpendingLimitChangeInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub squadsProposal: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigProgram: AccountMeta, + + pub vaultTransaction: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ExecuteSpendingLimitChange instruction + #[derive(Debug, Clone)] + pub struct ExecuteSpendingLimitChangeInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub squadsProposal: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigProgram: Pubkey, + + pub vaultTransaction: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ExecuteSpendingLimitChangeInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + squadsProposal: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigProgram: Pubkey, + + vaultTransaction: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + squadsProposal, + + squadsMultisig, + + squadsMultisigProgram, + + vaultTransaction, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ExecuteSpendingLimitChange + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ExecuteSpendingLimitChangeInstructionData {} + + impl ExecuteSpendingLimitChangeInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ExecuteSpendingLimitChangeInstruction + impl ExecuteSpendingLimitChangeInstruction { + fn discriminator() -> [u8; 8] { + [146u8, 175u8, 145u8, 31u8, 184u8, 129u8, 252u8, 79u8] + } + + pub fn data(data: ExecuteSpendingLimitChangeInstructionData) -> Self { + Self { + accounts: ExecuteSpendingLimitChangeInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ExecuteSpendingLimitChangeInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.squadsProposal = AccountMeta::new(accounts.squadsProposal, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigProgram = + AccountMeta::new_readonly(accounts.squadsMultisigProgram, false); + + self.accounts.vaultTransaction = + AccountMeta::new_readonly(accounts.vaultTransaction, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.squadsProposal.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigProgram.clone()); + + metas.push(self.accounts.vaultTransaction.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: SponsorProposal + // .................................................................... + + /// Main instruction struct for SponsorProposal + pub struct SponsorProposalInstruction { + pub accounts: SponsorProposalInstructionAccountMetas, + pub data: SponsorProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for SponsorProposal instruction + #[derive(Debug, Clone, Default)] + pub struct SponsorProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub teamAddress: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for SponsorProposal instruction + #[derive(Debug, Clone)] + pub struct SponsorProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub teamAddress: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl SponsorProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + teamAddress: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + teamAddress, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for SponsorProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct SponsorProposalInstructionData {} + + impl SponsorProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for SponsorProposalInstruction + impl SponsorProposalInstruction { + fn discriminator() -> [u8; 8] { + [193u8, 57u8, 170u8, 136u8, 101u8, 196u8, 58u8, 173u8] + } + + pub fn data(data: SponsorProposalInstructionData) -> Self { + Self { + accounts: SponsorProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: SponsorProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.teamAddress = AccountMeta::new_readonly(accounts.teamAddress, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.teamAddress.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CollectMeteoraDammFees + // .................................................................... + + /// Main instruction struct for CollectMeteoraDammFees + pub struct CollectMeteoraDammFeesInstruction { + pub accounts: CollectMeteoraDammFeesInstructionAccountMetas, + pub data: CollectMeteoraDammFeesInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CollectMeteoraDammFees instruction + #[derive(Debug, Clone, Default)] + pub struct CollectMeteoraDammFeesInstructionAccountMetas { + pub dao: AccountMeta, + + pub admin: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigVault: AccountMeta, + + pub squadsMultisigVaultTransaction: AccountMeta, + + pub squadsMultisigProposal: AccountMeta, + + pub squadsMultisigPermissionlessAccount: AccountMeta, + + pub meteoraClaimPositionFeesAccounts: + MeteoraClaimPositionFeesAccountsInstructionAccountMetas, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub squadsProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CollectMeteoraDammFees instruction + #[derive(Debug, Clone)] + pub struct CollectMeteoraDammFeesInstructionAccounts { + pub dao: Pubkey, + + pub admin: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub squadsMultisigVaultTransaction: Pubkey, + + pub squadsMultisigProposal: Pubkey, + + pub squadsMultisigPermissionlessAccount: Pubkey, + + pub meteoraClaimPositionFeesAccounts: MeteoraClaimPositionFeesAccountsInstructionAccounts, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub squadsProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CollectMeteoraDammFeesInstructionAccounts { + pub fn new( + dao: Pubkey, + + admin: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigVault: Pubkey, + + squadsMultisigVaultTransaction: Pubkey, + + squadsMultisigProposal: Pubkey, + + squadsMultisigPermissionlessAccount: Pubkey, + + meteoraClaimPositionFeesAccounts: MeteoraClaimPositionFeesAccountsInstructionAccounts, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + squadsProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + admin, + + squadsMultisig, + + squadsMultisigVault, + + squadsMultisigVaultTransaction, + + squadsMultisigProposal, + + squadsMultisigPermissionlessAccount, + + meteoraClaimPositionFeesAccounts, + + systemProgram, + + tokenProgram, + + squadsProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CollectMeteoraDammFees + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CollectMeteoraDammFeesInstructionData {} + + impl CollectMeteoraDammFeesInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CollectMeteoraDammFeesInstruction + impl CollectMeteoraDammFeesInstruction { + fn discriminator() -> [u8; 8] { + [139u8, 212u8, 105u8, 118u8, 126u8, 54u8, 214u8, 143u8] + } + + pub fn data(data: CollectMeteoraDammFeesInstructionData) -> Self { + Self { + accounts: CollectMeteoraDammFeesInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CollectMeteoraDammFeesInstructionAccounts) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.admin = AccountMeta::new(accounts.admin, true); + + self.accounts.squadsMultisig = AccountMeta::new(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigVault = + AccountMeta::new_readonly(accounts.squadsMultisigVault, false); + + self.accounts.squadsMultisigVaultTransaction = + AccountMeta::new(accounts.squadsMultisigVaultTransaction, false); + + self.accounts.squadsMultisigProposal = + AccountMeta::new(accounts.squadsMultisigProposal, false); + + self.accounts.squadsMultisigPermissionlessAccount = + AccountMeta::new_readonly(accounts.squadsMultisigPermissionlessAccount, true); + + self.accounts + .meteoraClaimPositionFeesAccounts + .set_from_accounts(accounts.meteoraClaimPositionFeesAccounts); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.squadsProgram = AccountMeta::new_readonly(accounts.squadsProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigVault.clone()); + + metas.push(self.accounts.squadsMultisigVaultTransaction.clone()); + + metas.push(self.accounts.squadsMultisigProposal.clone()); + + metas.push(self.accounts.squadsMultisigPermissionlessAccount.clone()); + + metas.extend( + self.accounts + .meteoraClaimPositionFeesAccounts + .to_account_metas(), + ); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.squadsProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: AdminApproveExecuteMultisigProposal + // .................................................................... + + /// Main instruction struct for AdminApproveExecuteMultisigProposal + pub struct AdminApproveExecuteMultisigProposalInstruction { + pub accounts: AdminApproveExecuteMultisigProposalInstructionAccountMetas, + pub data: AdminApproveExecuteMultisigProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for AdminApproveExecuteMultisigProposal instruction + #[derive(Debug, Clone, Default)] + pub struct AdminApproveExecuteMultisigProposalInstructionAccountMetas { + pub dao: AccountMeta, + + pub admin: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigProposal: AccountMeta, + + pub squadsMultisigVaultTransaction: AccountMeta, + + pub squadsMultisigProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for AdminApproveExecuteMultisigProposal instruction + #[derive(Debug, Clone)] + pub struct AdminApproveExecuteMultisigProposalInstructionAccounts { + pub dao: Pubkey, + + pub admin: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigProposal: Pubkey, + + pub squadsMultisigVaultTransaction: Pubkey, + + pub squadsMultisigProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl AdminApproveExecuteMultisigProposalInstructionAccounts { + pub fn new( + dao: Pubkey, + + admin: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigProposal: Pubkey, + + squadsMultisigVaultTransaction: Pubkey, + + squadsMultisigProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + dao, + + admin, + + squadsMultisig, + + squadsMultisigProposal, + + squadsMultisigVaultTransaction, + + squadsMultisigProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for AdminApproveExecuteMultisigProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct AdminApproveExecuteMultisigProposalInstructionData {} + + impl AdminApproveExecuteMultisigProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for AdminApproveExecuteMultisigProposalInstruction + impl AdminApproveExecuteMultisigProposalInstruction { + fn discriminator() -> [u8; 8] { + [99u8, 14u8, 66u8, 64u8, 95u8, 59u8, 11u8, 96u8] + } + + pub fn data(data: AdminApproveExecuteMultisigProposalInstructionData) -> Self { + Self { + accounts: AdminApproveExecuteMultisigProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: AdminApproveExecuteMultisigProposalInstructionAccounts, + ) -> Self { + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.admin = AccountMeta::new(accounts.admin, true); + + self.accounts.squadsMultisig = AccountMeta::new(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigProposal = + AccountMeta::new(accounts.squadsMultisigProposal, false); + + self.accounts.squadsMultisigVaultTransaction = + AccountMeta::new(accounts.squadsMultisigVaultTransaction, false); + + self.accounts.squadsMultisigProgram = + AccountMeta::new_readonly(accounts.squadsMultisigProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigProposal.clone()); + + metas.push(self.accounts.squadsMultisigVaultTransaction.clone()); + + metas.push(self.accounts.squadsMultisigProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: AdminCancelProposal + // .................................................................... + + /// Main instruction struct for AdminCancelProposal + pub struct AdminCancelProposalInstruction { + pub accounts: AdminCancelProposalInstructionAccountMetas, + pub data: AdminCancelProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for AdminCancelProposal instruction + #[derive(Debug, Clone, Default)] + pub struct AdminCancelProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub question: AccountMeta, + + pub squadsProposal: AccountMeta, + + pub squadsMultisig: AccountMeta, + + pub squadsMultisigProgram: AccountMeta, + + pub ammPassBaseVault: AccountMeta, + + pub ammPassQuoteVault: AccountMeta, + + pub ammFailBaseVault: AccountMeta, + + pub ammFailQuoteVault: AccountMeta, + + pub ammBaseVault: AccountMeta, + + pub ammQuoteVault: AccountMeta, + + pub vaultProgram: AccountMeta, + + pub vaultEventAuthority: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub quoteVault: AccountMeta, + + pub quoteVaultUnderlyingTokenAccount: AccountMeta, + + pub passQuoteMint: AccountMeta, + + pub failQuoteMint: AccountMeta, + + pub passBaseMint: AccountMeta, + + pub failBaseMint: AccountMeta, + + pub baseVault: AccountMeta, + + pub baseVaultUnderlyingTokenAccount: AccountMeta, + + pub admin: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for AdminCancelProposal instruction + #[derive(Debug, Clone)] + pub struct AdminCancelProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub question: Pubkey, + + pub squadsProposal: Pubkey, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigProgram: Pubkey, + + pub ammPassBaseVault: Pubkey, + + pub ammPassQuoteVault: Pubkey, + + pub ammFailBaseVault: Pubkey, + + pub ammFailQuoteVault: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + + pub vaultProgram: Pubkey, + + pub vaultEventAuthority: Pubkey, + + pub tokenProgram: Pubkey, + + pub quoteVault: Pubkey, + + pub quoteVaultUnderlyingTokenAccount: Pubkey, + + pub passQuoteMint: Pubkey, + + pub failQuoteMint: Pubkey, + + pub passBaseMint: Pubkey, + + pub failBaseMint: Pubkey, + + pub baseVault: Pubkey, + + pub baseVaultUnderlyingTokenAccount: Pubkey, + + pub admin: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl AdminCancelProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + question: Pubkey, + + squadsProposal: Pubkey, + + squadsMultisig: Pubkey, + + squadsMultisigProgram: Pubkey, + + ammPassBaseVault: Pubkey, + + ammPassQuoteVault: Pubkey, + + ammFailBaseVault: Pubkey, + + ammFailQuoteVault: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + + vaultProgram: Pubkey, + + vaultEventAuthority: Pubkey, + + tokenProgram: Pubkey, + + quoteVault: Pubkey, + + quoteVaultUnderlyingTokenAccount: Pubkey, + + passQuoteMint: Pubkey, + + failQuoteMint: Pubkey, + + passBaseMint: Pubkey, + + failBaseMint: Pubkey, + + baseVault: Pubkey, + + baseVaultUnderlyingTokenAccount: Pubkey, + + admin: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + question, + + squadsProposal, + + squadsMultisig, + + squadsMultisigProgram, + + ammPassBaseVault, + + ammPassQuoteVault, + + ammFailBaseVault, + + ammFailQuoteVault, + + ammBaseVault, + + ammQuoteVault, + + vaultProgram, + + vaultEventAuthority, + + tokenProgram, + + quoteVault, + + quoteVaultUnderlyingTokenAccount, + + passQuoteMint, + + failQuoteMint, + + passBaseMint, + + failBaseMint, + + baseVault, + + baseVaultUnderlyingTokenAccount, + + admin, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for AdminCancelProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct AdminCancelProposalInstructionData {} + + impl AdminCancelProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for AdminCancelProposalInstruction + impl AdminCancelProposalInstruction { + fn discriminator() -> [u8; 8] { + [95u8, 233u8, 121u8, 193u8, 90u8, 80u8, 147u8, 255u8] + } + + pub fn data(data: AdminCancelProposalInstructionData) -> Self { + Self { + accounts: AdminCancelProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: AdminCancelProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.question = AccountMeta::new(accounts.question, false); + + self.accounts.squadsProposal = AccountMeta::new(accounts.squadsProposal, false); + + self.accounts.squadsMultisig = + AccountMeta::new_readonly(accounts.squadsMultisig, false); + + self.accounts.squadsMultisigProgram = + AccountMeta::new_readonly(accounts.squadsMultisigProgram, false); + + self.accounts.ammPassBaseVault = AccountMeta::new(accounts.ammPassBaseVault, false); + + self.accounts.ammPassQuoteVault = AccountMeta::new(accounts.ammPassQuoteVault, false); + + self.accounts.ammFailBaseVault = AccountMeta::new(accounts.ammFailBaseVault, false); + + self.accounts.ammFailQuoteVault = AccountMeta::new(accounts.ammFailQuoteVault, false); + + self.accounts.ammBaseVault = AccountMeta::new(accounts.ammBaseVault, false); + + self.accounts.ammQuoteVault = AccountMeta::new(accounts.ammQuoteVault, false); + + self.accounts.vaultProgram = AccountMeta::new_readonly(accounts.vaultProgram, false); + + self.accounts.vaultEventAuthority = + AccountMeta::new_readonly(accounts.vaultEventAuthority, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.quoteVault = AccountMeta::new(accounts.quoteVault, false); + + self.accounts.quoteVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.quoteVaultUnderlyingTokenAccount, false); + + self.accounts.passQuoteMint = AccountMeta::new(accounts.passQuoteMint, false); + + self.accounts.failQuoteMint = AccountMeta::new(accounts.failQuoteMint, false); + + self.accounts.passBaseMint = AccountMeta::new(accounts.passBaseMint, false); + + self.accounts.failBaseMint = AccountMeta::new(accounts.failBaseMint, false); + + self.accounts.baseVault = AccountMeta::new(accounts.baseVault, false); + + self.accounts.baseVaultUnderlyingTokenAccount = + AccountMeta::new(accounts.baseVaultUnderlyingTokenAccount, false); + + self.accounts.admin = AccountMeta::new(accounts.admin, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.question.clone()); + + metas.push(self.accounts.squadsProposal.clone()); + + metas.push(self.accounts.squadsMultisig.clone()); + + metas.push(self.accounts.squadsMultisigProgram.clone()); + + metas.push(self.accounts.ammPassBaseVault.clone()); + + metas.push(self.accounts.ammPassQuoteVault.clone()); + + metas.push(self.accounts.ammFailBaseVault.clone()); + + metas.push(self.accounts.ammFailQuoteVault.clone()); + + metas.push(self.accounts.ammBaseVault.clone()); + + metas.push(self.accounts.ammQuoteVault.clone()); + + metas.push(self.accounts.vaultProgram.clone()); + + metas.push(self.accounts.vaultEventAuthority.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.quoteVault.clone()); + + metas.push(self.accounts.quoteVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.passQuoteMint.clone()); + + metas.push(self.accounts.failQuoteMint.clone()); + + metas.push(self.accounts.passBaseMint.clone()); + + metas.push(self.accounts.failBaseMint.clone()); + + metas.push(self.accounts.baseVault.clone()); + + metas.push(self.accounts.baseVaultUnderlyingTokenAccount.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: AdminRemoveProposal + // .................................................................... + + /// Main instruction struct for AdminRemoveProposal + pub struct AdminRemoveProposalInstruction { + pub accounts: AdminRemoveProposalInstructionAccountMetas, + pub data: AdminRemoveProposalInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for AdminRemoveProposal instruction + #[derive(Debug, Clone, Default)] + pub struct AdminRemoveProposalInstructionAccountMetas { + pub proposal: AccountMeta, + + pub dao: AccountMeta, + + pub admin: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for AdminRemoveProposal instruction + #[derive(Debug, Clone)] + pub struct AdminRemoveProposalInstructionAccounts { + pub proposal: Pubkey, + + pub dao: Pubkey, + + pub admin: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl AdminRemoveProposalInstructionAccounts { + pub fn new( + proposal: Pubkey, + + dao: Pubkey, + + admin: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + proposal, + + dao, + + admin, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for AdminRemoveProposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct AdminRemoveProposalInstructionData {} + + impl AdminRemoveProposalInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for AdminRemoveProposalInstruction + impl AdminRemoveProposalInstruction { + fn discriminator() -> [u8; 8] { + [242u8, 199u8, 27u8, 28u8, 7u8, 108u8, 122u8, 73u8] + } + + pub fn data(data: AdminRemoveProposalInstructionData) -> Self { + Self { + accounts: AdminRemoveProposalInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: AdminRemoveProposalInstructionAccounts) -> Self { + self.accounts.proposal = AccountMeta::new(accounts.proposal, false); + + self.accounts.dao = AccountMeta::new(accounts.dao, false); + + self.accounts.admin = AccountMeta::new(accounts.admin, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.proposal.clone()); + + metas.push(self.accounts.dao.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // ------------------------------------------------------------------------ + // Data Accounts (with discriminators) + // ------------------------------------------------------------------------ + + /// AccountDiscriminator implementation for AmmPosition + impl AccountDiscriminator for AmmPosition { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for Dao + impl AccountDiscriminator for Dao { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for Proposal + impl AccountDiscriminator for Proposal { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for StakeAccount + impl AccountDiscriminator for StakeAccount { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + // ------------------------------------------------------------------------ + // Errors + // ------------------------------------------------------------------------ + + /// Program errors for futarchy + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum FutarchyError { + /// Amms must have been created within 5 minutes (counted in slots) of + /// proposal initialization + AmmTooOld = 6000, + + /// An amm has an `initial_observation` that doesn't match the `dao`'s + /// config + InvalidInitialObservation = 6001, + + /// An amm has a `max_observation_change_per_update` that doesn't match + /// the `dao`'s config + InvalidMaxObservationChange = 6002, + + /// An amm has a `start_delay_slots` that doesn't match the `dao`'s + /// config + InvalidStartDelaySlots = 6003, + + /// One of the vaults has an invalid `settlement_authority` + InvalidSettlementAuthority = 6004, + + /// Proposal is too young to be executed or rejected + ProposalTooYoung = 6005, + + /// Markets too young for proposal to be finalized. TWAP might need to + /// be cranked + MarketsTooYoung = 6006, + + /// This proposal has already been finalized + ProposalAlreadyFinalized = 6007, + + /// A conditional vault has an invalid nonce. A nonce should encode the + /// proposal number + InvalidVaultNonce = 6008, + + /// This proposal can't be executed because it isn't in the passed state + ProposalNotPassed = 6009, + + /// More liquidity needs to be in the AMM to launch this proposal + InsufficientLiquidity = 6010, + + /// Proposal duration must be longer 1 day and longer than 2 times the + /// TWAP start delay + ProposalDurationTooShort = 6011, + + /// Pass threshold must be less than 10% + PassThresholdTooHigh = 6012, + + /// Question must have exactly 2 outcomes for binary futarchy + QuestionMustBeBinary = 6013, + + /// Squads proposal must be in Draft status + InvalidSquadsProposalStatus = 6014, + + /// Casting overflow. If you're seeing this, please report this + CastingOverflow = 6015, + + /// Insufficient balance + InsufficientBalance = 6016, + + /// Cannot remove zero liquidity + ZeroLiquidityRemove = 6017, + + /// Swap slippage exceeded + SwapSlippageExceeded = 6018, + + /// Assert failed + AssertFailed = 6019, + + /// Invalid admin + InvalidAdmin = 6020, + + /// Proposal is not in draft state + ProposalNotInDraftState = 6021, + + /// Insufficient token balance + InsufficientTokenBalance = 6022, + + /// Invalid amount + InvalidAmount = 6023, + + /// Insufficient stake to launch proposal + InsufficientStakeToLaunch = 6024, + + /// Staker not found in proposal + StakerNotFound = 6025, + + /// Pool must be in spot state + PoolNotInSpotState = 6026, + + /// If you're providing liquidity, you must provide both base and quote + /// token accounts + InvalidDaoCreateLiquidity = 6027, + + /// Invalid stake account + InvalidStakeAccount = 6028, + + /// An invariant was violated. You should get in contact with the + /// MetaDAO team if you see this + InvariantViolated = 6029, + + /// Proposal needs to be active to perform a conditional swap + ProposalNotActive = 6030, + + /// This Squads transaction should only contain calls to update spending + /// limits + InvalidTransaction = 6031, + + /// Proposal has already been sponsored + ProposalAlreadySponsored = 6032, + + /// Team sponsored pass threshold must be between -10% and 10% + InvalidTeamSponsoredPassThreshold = 6033, + + /// Target K must be greater than the current K + InvalidTargetK = 6034, + + /// Failed to compile transaction message for Squads vault transaction + InvalidTransactionMessage = 6035, + } + + impl FutarchyError { + /// Get the error code + pub fn code(&self) -> u32 { + *self as u32 + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + match self { + + Self::AmmTooOld => "Amms must have been created within 5 minutes (counted in slots) of proposal initialization", + + Self::InvalidInitialObservation => "An amm has an `initial_observation` that doesn't match the `dao`'s config", + + Self::InvalidMaxObservationChange => "An amm has a `max_observation_change_per_update` that doesn't match the `dao`'s config", + + Self::InvalidStartDelaySlots => "An amm has a `start_delay_slots` that doesn't match the `dao`'s config", + + Self::InvalidSettlementAuthority => "One of the vaults has an invalid `settlement_authority`", + + Self::ProposalTooYoung => "Proposal is too young to be executed or rejected", + + Self::MarketsTooYoung => "Markets too young for proposal to be finalized. TWAP might need to be cranked", + + Self::ProposalAlreadyFinalized => "This proposal has already been finalized", + + Self::InvalidVaultNonce => "A conditional vault has an invalid nonce. A nonce should encode the proposal number", + + Self::ProposalNotPassed => "This proposal can't be executed because it isn't in the passed state", + + Self::InsufficientLiquidity => "More liquidity needs to be in the AMM to launch this proposal", + + Self::ProposalDurationTooShort => "Proposal duration must be longer 1 day and longer than 2 times the TWAP start delay", + + Self::PassThresholdTooHigh => "Pass threshold must be less than 10%", + + Self::QuestionMustBeBinary => "Question must have exactly 2 outcomes for binary futarchy", + + Self::InvalidSquadsProposalStatus => "Squads proposal must be in Draft status", + + Self::CastingOverflow => "Casting overflow. If you're seeing this, please report this", + + Self::InsufficientBalance => "Insufficient balance", + + Self::ZeroLiquidityRemove => "Cannot remove zero liquidity", + + Self::SwapSlippageExceeded => "Swap slippage exceeded", + + Self::AssertFailed => "Assert failed", + + Self::InvalidAdmin => "Invalid admin", + + Self::ProposalNotInDraftState => "Proposal is not in draft state", + + Self::InsufficientTokenBalance => "Insufficient token balance", + + Self::InvalidAmount => "Invalid amount", + + Self::InsufficientStakeToLaunch => "Insufficient stake to launch proposal", + + Self::StakerNotFound => "Staker not found in proposal", + + Self::PoolNotInSpotState => "Pool must be in spot state", + + Self::InvalidDaoCreateLiquidity => "If you're providing liquidity, you must provide both base and quote token accounts", + + Self::InvalidStakeAccount => "Invalid stake account", + + Self::InvariantViolated => "An invariant was violated. You should get in contact with the MetaDAO team if you see this", + + Self::ProposalNotActive => "Proposal needs to be active to perform a conditional swap", + + Self::InvalidTransaction => "This Squads transaction should only contain calls to update spending limits", + + Self::ProposalAlreadySponsored => "Proposal has already been sponsored", + + Self::InvalidTeamSponsoredPassThreshold => "Team sponsored pass threshold must be between -10% and 10%", + + Self::InvalidTargetK => "Target K must be greater than the current K", + + Self::InvalidTransactionMessage => "Failed to compile transaction message for Squads vault transaction", + + } + } + + /// Try to convert from error code + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::AmmTooOld), + + 6001 => Some(Self::InvalidInitialObservation), + + 6002 => Some(Self::InvalidMaxObservationChange), + + 6003 => Some(Self::InvalidStartDelaySlots), + + 6004 => Some(Self::InvalidSettlementAuthority), + + 6005 => Some(Self::ProposalTooYoung), + + 6006 => Some(Self::MarketsTooYoung), + + 6007 => Some(Self::ProposalAlreadyFinalized), + + 6008 => Some(Self::InvalidVaultNonce), + + 6009 => Some(Self::ProposalNotPassed), + + 6010 => Some(Self::InsufficientLiquidity), + + 6011 => Some(Self::ProposalDurationTooShort), + + 6012 => Some(Self::PassThresholdTooHigh), + + 6013 => Some(Self::QuestionMustBeBinary), + + 6014 => Some(Self::InvalidSquadsProposalStatus), + + 6015 => Some(Self::CastingOverflow), + + 6016 => Some(Self::InsufficientBalance), + + 6017 => Some(Self::ZeroLiquidityRemove), + + 6018 => Some(Self::SwapSlippageExceeded), + + 6019 => Some(Self::AssertFailed), + + 6020 => Some(Self::InvalidAdmin), + + 6021 => Some(Self::ProposalNotInDraftState), + + 6022 => Some(Self::InsufficientTokenBalance), + + 6023 => Some(Self::InvalidAmount), + + 6024 => Some(Self::InsufficientStakeToLaunch), + + 6025 => Some(Self::StakerNotFound), + + 6026 => Some(Self::PoolNotInSpotState), + + 6027 => Some(Self::InvalidDaoCreateLiquidity), + + 6028 => Some(Self::InvalidStakeAccount), + + 6029 => Some(Self::InvariantViolated), + + 6030 => Some(Self::ProposalNotActive), + + 6031 => Some(Self::InvalidTransaction), + + 6032 => Some(Self::ProposalAlreadySponsored), + + 6033 => Some(Self::InvalidTeamSponsoredPassThreshold), + + 6034 => Some(Self::InvalidTargetK), + + 6035 => Some(Self::InvalidTransactionMessage), + + _ => None, + } + } + } + + impl std::fmt::Display for FutarchyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.code(), self.msg()) + } + } + + impl std::error::Error for FutarchyError {} + + // ------------------------------------------------------------------------ + // Composite Accounts + // ------------------------------------------------------------------------ + + /// Composite Account: meteoraClaimPositionFeesAccounts + #[derive(Debug, Clone, Default)] + pub struct MeteoraClaimPositionFeesAccountsInstructionAccountMetas { + pub dammV2Program: AccountMeta, + + pub dammV2EventAuthority: AccountMeta, + + pub poolAuthority: AccountMeta, + + pub pool: AccountMeta, + + pub position: AccountMeta, + + pub tokenAAccount: AccountMeta, + + pub tokenBAccount: AccountMeta, + + pub tokenAVault: AccountMeta, + + pub tokenBVault: AccountMeta, + + pub tokenAMint: AccountMeta, + + pub tokenBMint: AccountMeta, + + pub positionNftAccount: AccountMeta, + + pub owner: AccountMeta, + + pub tokenAProgram: AccountMeta, + + pub tokenBProgram: AccountMeta, + } + + impl MeteoraClaimPositionFeesAccountsInstructionAccountMetas { + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.dammV2Program.clone()); + + metas.push(self.dammV2EventAuthority.clone()); + + metas.push(self.poolAuthority.clone()); + + metas.push(self.pool.clone()); + + metas.push(self.position.clone()); + + metas.push(self.tokenAAccount.clone()); + + metas.push(self.tokenBAccount.clone()); + + metas.push(self.tokenAVault.clone()); + + metas.push(self.tokenBVault.clone()); + + metas.push(self.tokenAMint.clone()); + + metas.push(self.tokenBMint.clone()); + + metas.push(self.positionNftAccount.clone()); + + metas.push(self.owner.clone()); + + metas.push(self.tokenAProgram.clone()); + + metas.push(self.tokenBProgram.clone()); + + metas + } + + fn set_from_accounts( + &mut self, + accounts: MeteoraClaimPositionFeesAccountsInstructionAccounts, + ) { + self.dammV2Program = AccountMeta::new_readonly(accounts.dammV2Program, false); + + self.dammV2EventAuthority = + AccountMeta::new_readonly(accounts.dammV2EventAuthority, false); + + self.poolAuthority = AccountMeta::new_readonly(accounts.poolAuthority, false); + + self.pool = AccountMeta::new_readonly(accounts.pool, false); + + self.position = AccountMeta::new(accounts.position, false); + + self.tokenAAccount = AccountMeta::new(accounts.tokenAAccount, false); + + self.tokenBAccount = AccountMeta::new(accounts.tokenBAccount, false); + + self.tokenAVault = AccountMeta::new(accounts.tokenAVault, false); + + self.tokenBVault = AccountMeta::new(accounts.tokenBVault, false); + + self.tokenAMint = AccountMeta::new_readonly(accounts.tokenAMint, false); + + self.tokenBMint = AccountMeta::new_readonly(accounts.tokenBMint, false); + + self.positionNftAccount = AccountMeta::new_readonly(accounts.positionNftAccount, false); + + self.owner = AccountMeta::new_readonly(accounts.owner, false); + + self.tokenAProgram = AccountMeta::new_readonly(accounts.tokenAProgram, false); + + self.tokenBProgram = AccountMeta::new_readonly(accounts.tokenBProgram, false); + } + } + + #[derive(Debug, Clone)] + pub struct MeteoraClaimPositionFeesAccountsInstructionAccounts { + pub dammV2Program: Pubkey, + + pub dammV2EventAuthority: Pubkey, + + pub poolAuthority: Pubkey, + + pub pool: Pubkey, + + pub position: Pubkey, + + pub tokenAAccount: Pubkey, + + pub tokenBAccount: Pubkey, + + pub tokenAVault: Pubkey, + + pub tokenBVault: Pubkey, + + pub tokenAMint: Pubkey, + + pub tokenBMint: Pubkey, + + pub positionNftAccount: Pubkey, + + pub owner: Pubkey, + + pub tokenAProgram: Pubkey, + + pub tokenBProgram: Pubkey, + } + + impl MeteoraClaimPositionFeesAccountsInstructionAccounts { + pub fn new( + dammV2Program: Pubkey, + + dammV2EventAuthority: Pubkey, + + poolAuthority: Pubkey, + + pool: Pubkey, + + position: Pubkey, + + tokenAAccount: Pubkey, + + tokenBAccount: Pubkey, + + tokenAVault: Pubkey, + + tokenBVault: Pubkey, + + tokenAMint: Pubkey, + + tokenBMint: Pubkey, + + positionNftAccount: Pubkey, + + owner: Pubkey, + + tokenAProgram: Pubkey, + + tokenBProgram: Pubkey, + ) -> Self { + Self { + dammV2Program, + + dammV2EventAuthority, + + poolAuthority, + + pool, + + position, + + tokenAAccount, + + tokenBAccount, + + tokenAVault, + + tokenBVault, + + tokenAMint, + + tokenBMint, + + positionNftAccount, + + owner, + + tokenAProgram, + + tokenBProgram, + } + } + } + + // ------------------------------------------------------------------------ + // Custom Types + // ------------------------------------------------------------------------ + + /// Custom struct: CommonFields + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct CommonFields { + pub slot: u64, + + pub unixTimestamp: i64, + + pub daoSeqNum: u64, + } + + impl CommonFields { + pub fn new(slot: u64, unixTimestamp: i64, daoSeqNum: u64) -> Self { + Self { + slot, + + unixTimestamp, + + daoSeqNum, + } + } + } + + /// Custom struct: ConditionalSwapParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ConditionalSwapParams { + pub market: Market, + + pub swapType: SwapType, + + pub inputAmount: u64, + + pub minOutputAmount: u64, + } + + impl ConditionalSwapParams { + pub fn new( + market: Market, + + swapType: SwapType, + + inputAmount: u64, + + minOutputAmount: u64, + ) -> Self { + Self { + market, + + swapType, + + inputAmount, + + minOutputAmount, + } + } + } + + /// Custom struct: InitializeDaoParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitializeDaoParams { + pub twapInitialObservation: u128, + + pub twapMaxObservationChangePerUpdate: u128, + + pub twapStartDelaySeconds: u32, + + pub minQuoteFutarchicLiquidity: u64, + + pub minBaseFutarchicLiquidity: u64, + + pub baseToStake: u64, + + pub passThresholdBps: u16, + + pub secondsPerProposal: u32, + + pub nonce: u64, + + pub initialSpendingLimit: Option, + + pub teamSponsoredPassThresholdBps: i16, + + pub teamAddress: Pubkey, + } + + impl InitializeDaoParams { + pub fn new( + twapInitialObservation: u128, + + twapMaxObservationChangePerUpdate: u128, + + twapStartDelaySeconds: u32, + + minQuoteFutarchicLiquidity: u64, + + minBaseFutarchicLiquidity: u64, + + baseToStake: u64, + + passThresholdBps: u16, + + secondsPerProposal: u32, + + nonce: u64, + + initialSpendingLimit: Option, + + teamSponsoredPassThresholdBps: i16, + + teamAddress: Pubkey, + ) -> Self { + Self { + twapInitialObservation, + + twapMaxObservationChangePerUpdate, + + twapStartDelaySeconds, + + minQuoteFutarchicLiquidity, + + minBaseFutarchicLiquidity, + + baseToStake, + + passThresholdBps, + + secondsPerProposal, + + nonce, + + initialSpendingLimit, + + teamSponsoredPassThresholdBps, + + teamAddress, + } + } + } + + /// Custom struct: ProvideLiquidityParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ProvideLiquidityParams { + pub quoteAmount: u64, + + pub maxBaseAmount: u64, + + pub minLiquidity: u128, + + pub positionAuthority: Pubkey, + } + + impl ProvideLiquidityParams { + pub fn new( + quoteAmount: u64, + + maxBaseAmount: u64, + + minLiquidity: u128, + + positionAuthority: Pubkey, + ) -> Self { + Self { + quoteAmount, + + maxBaseAmount, + + minLiquidity, + + positionAuthority, + } + } + } + + /// Custom struct: SpotSwapParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct SpotSwapParams { + pub inputAmount: u64, + + pub swapType: SwapType, + + pub minOutputAmount: u64, + } + + impl SpotSwapParams { + pub fn new(inputAmount: u64, swapType: SwapType, minOutputAmount: u64) -> Self { + Self { + inputAmount, + + swapType, + + minOutputAmount, + } + } + } + + /// Custom struct: StakeToProposalParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct StakeToProposalParams { + pub amount: u64, + } + + impl StakeToProposalParams { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + /// Custom struct: UnstakeFromProposalParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct UnstakeFromProposalParams { + pub amount: u64, + } + + impl UnstakeFromProposalParams { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + /// Custom struct: UpdateDaoParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct UpdateDaoParams { + pub passThresholdBps: Option, + + pub secondsPerProposal: Option, + + pub twapInitialObservation: Option, + + pub twapMaxObservationChangePerUpdate: Option, + + pub twapStartDelaySeconds: Option, + + pub minQuoteFutarchicLiquidity: Option, + + pub minBaseFutarchicLiquidity: Option, + + pub baseToStake: Option, + + pub teamSponsoredPassThresholdBps: Option, + + pub teamAddress: Option, + } + + impl UpdateDaoParams { + pub fn new( + passThresholdBps: Option, + + secondsPerProposal: Option, + + twapInitialObservation: Option, + + twapMaxObservationChangePerUpdate: Option, + + twapStartDelaySeconds: Option, + + minQuoteFutarchicLiquidity: Option, + + minBaseFutarchicLiquidity: Option, + + baseToStake: Option, + + teamSponsoredPassThresholdBps: Option, + + teamAddress: Option, + ) -> Self { + Self { + passThresholdBps, + + secondsPerProposal, + + twapInitialObservation, + + twapMaxObservationChangePerUpdate, + + twapStartDelaySeconds, + + minQuoteFutarchicLiquidity, + + minBaseFutarchicLiquidity, + + baseToStake, + + teamSponsoredPassThresholdBps, + + teamAddress, + } + } + } + + /// Custom struct: WithdrawLiquidityParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct WithdrawLiquidityParams { + pub liquidityToWithdraw: u128, + + pub minBaseAmount: u64, + + pub minQuoteAmount: u64, + } + + impl WithdrawLiquidityParams { + pub fn new(liquidityToWithdraw: u128, minBaseAmount: u64, minQuoteAmount: u64) -> Self { + Self { + liquidityToWithdraw, + + minBaseAmount, + + minQuoteAmount, + } + } + } + + /// Custom struct: InitialSpendingLimit + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitialSpendingLimit { + pub amountPerMonth: u64, + + pub members: Vec, + } + + impl InitialSpendingLimit { + pub fn new(amountPerMonth: u64, members: Vec) -> Self { + Self { + amountPerMonth, + + members, + } + } + } + + /// Custom struct: FutarchyAmm + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct FutarchyAmm { + pub state: PoolState, + + pub totalLiquidity: u128, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub ammBaseVault: Pubkey, + + pub ammQuoteVault: Pubkey, + } + + impl FutarchyAmm { + pub fn new( + state: PoolState, + + totalLiquidity: u128, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + ammBaseVault: Pubkey, + + ammQuoteVault: Pubkey, + ) -> Self { + Self { + state, + + totalLiquidity, + + baseMint, + + quoteMint, + + ammBaseVault, + + ammQuoteVault, + } + } + } + + /// Custom struct: TwapOracle + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct TwapOracle { + pub aggregator: u128, + + pub lastUpdatedTimestamp: i64, + + pub createdAtTimestamp: i64, + + pub lastPrice: u128, + + pub lastObservation: u128, + + pub maxObservationChangePerUpdate: u128, + + pub initialObservation: u128, + + pub startDelaySeconds: u32, + } + + impl TwapOracle { + pub fn new( + aggregator: u128, + + lastUpdatedTimestamp: i64, + + createdAtTimestamp: i64, + + lastPrice: u128, + + lastObservation: u128, + + maxObservationChangePerUpdate: u128, + + initialObservation: u128, + + startDelaySeconds: u32, + ) -> Self { + Self { + aggregator, + + lastUpdatedTimestamp, + + createdAtTimestamp, + + lastPrice, + + lastObservation, + + maxObservationChangePerUpdate, + + initialObservation, + + startDelaySeconds, + } + } + } + + /// Custom struct: Pool + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Pool { + pub oracle: TwapOracle, + + pub quoteReserves: u64, + + pub baseReserves: u64, + + pub quoteProtocolFeeBalance: u64, + + pub baseProtocolFeeBalance: u64, + } + + impl Pool { + pub fn new( + oracle: TwapOracle, + + quoteReserves: u64, + + baseReserves: u64, + + quoteProtocolFeeBalance: u64, + + baseProtocolFeeBalance: u64, + ) -> Self { + Self { + oracle, + + quoteReserves, + + baseReserves, + + quoteProtocolFeeBalance, + + baseProtocolFeeBalance, + } + } + } + + /// Custom enum: PoolState + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum PoolState { + Spot { spot: Pool }, + + Futarchy { spot: Pool, pass: Pool, fail: Pool }, + } + + /// Custom enum: Market + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum Market { + Spot, + + Pass, + + Fail, + } + + /// Custom enum: SwapType + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum SwapType { + Buy, + + Sell, + } + + /// Custom enum: Token + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum Token { + Base, + + Quote, + } + + /// Custom enum: ProposalState + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum ProposalState { + Draft { amountStaked: u64 }, + + Pending, + + Passed, + + Failed, + + Removed, + } + + /// Custom struct: AmmPosition + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct AmmPosition { + pub dao: Pubkey, + + pub positionAuthority: Pubkey, + + pub liquidity: u128, + } + + impl AmmPosition { + pub fn new(dao: Pubkey, positionAuthority: Pubkey, liquidity: u128) -> Self { + Self { + dao, + + positionAuthority, + + liquidity, + } + } + } + + /// Custom struct: Dao + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Dao { + pub amm: FutarchyAmm, + + pub nonce: u64, + + pub daoCreator: Pubkey, + + pub pdaBump: u8, + + pub squadsMultisig: Pubkey, + + pub squadsMultisigVault: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub proposalCount: u32, + + pub passThresholdBps: u16, + + pub secondsPerProposal: u32, + + pub twapInitialObservation: u128, + + pub twapMaxObservationChangePerUpdate: u128, + + pub twapStartDelaySeconds: u32, + + pub minQuoteFutarchicLiquidity: u64, + + pub minBaseFutarchicLiquidity: u64, + + pub baseToStake: u64, + + pub seqNum: u64, + + pub initialSpendingLimit: Option, + + pub teamSponsoredPassThresholdBps: i16, + + pub teamAddress: Pubkey, + } + + impl Dao { + pub fn new( + amm: FutarchyAmm, + + nonce: u64, + + daoCreator: Pubkey, + + pdaBump: u8, + + squadsMultisig: Pubkey, + + squadsMultisigVault: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + proposalCount: u32, + + passThresholdBps: u16, + + secondsPerProposal: u32, + + twapInitialObservation: u128, + + twapMaxObservationChangePerUpdate: u128, + + twapStartDelaySeconds: u32, + + minQuoteFutarchicLiquidity: u64, + + minBaseFutarchicLiquidity: u64, + + baseToStake: u64, + + seqNum: u64, + + initialSpendingLimit: Option, + + teamSponsoredPassThresholdBps: i16, + + teamAddress: Pubkey, + ) -> Self { + Self { + amm, + + nonce, + + daoCreator, + + pdaBump, + + squadsMultisig, + + squadsMultisigVault, + + baseMint, + + quoteMint, + + proposalCount, + + passThresholdBps, + + secondsPerProposal, + + twapInitialObservation, + + twapMaxObservationChangePerUpdate, + + twapStartDelaySeconds, + + minQuoteFutarchicLiquidity, + + minBaseFutarchicLiquidity, + + baseToStake, + + seqNum, + + initialSpendingLimit, + + teamSponsoredPassThresholdBps, + + teamAddress, + } + } + } + + /// Custom struct: Proposal + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Proposal { + pub number: u32, + + pub proposer: Pubkey, + + pub timestampEnqueued: i64, + + pub state: ProposalState, + + pub baseVault: Pubkey, + + pub quoteVault: Pubkey, + + pub dao: Pubkey, + + pub pdaBump: u8, + + pub question: Pubkey, + + pub durationInSeconds: u32, + + pub squadsProposal: Pubkey, + + pub passBaseMint: Pubkey, + + pub passQuoteMint: Pubkey, + + pub failBaseMint: Pubkey, + + pub failQuoteMint: Pubkey, + + pub isTeamSponsored: bool, + } + + impl Proposal { + pub fn new( + number: u32, + + proposer: Pubkey, + + timestampEnqueued: i64, + + state: ProposalState, + + baseVault: Pubkey, + + quoteVault: Pubkey, + + dao: Pubkey, + + pdaBump: u8, + + question: Pubkey, + + durationInSeconds: u32, + + squadsProposal: Pubkey, + + passBaseMint: Pubkey, + + passQuoteMint: Pubkey, + + failBaseMint: Pubkey, + + failQuoteMint: Pubkey, + + isTeamSponsored: bool, + ) -> Self { + Self { + number, + + proposer, + + timestampEnqueued, + + state, + + baseVault, + + quoteVault, + + dao, + + pdaBump, + + question, + + durationInSeconds, + + squadsProposal, + + passBaseMint, + + passQuoteMint, + + failBaseMint, + + failQuoteMint, + + isTeamSponsored, + } + } + } + + /// Custom struct: StakeAccount + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct StakeAccount { + pub proposal: Pubkey, + + pub staker: Pubkey, + + pub amount: u64, + + pub bump: u8, + } + + impl StakeAccount { + pub fn new(proposal: Pubkey, staker: Pubkey, amount: u64, bump: u8) -> Self { + Self { + proposal, + + staker, + + amount, + + bump, + } + } + } +} + +// ---------------------------------------------------------------------------- +// Program: price_based_performance_package +// ---------------------------------------------------------------------------- +pub mod price_based_performance_package { + use super::*; + + // ------------------------------------------------------------------------ + // Program ID + // ------------------------------------------------------------------------ + + /// Returns the program ID for price_based_performance_package + pub fn program_id() -> Pubkey { + pubkey!("pbPPQH7jyKoSLu8QYs3rSY3YkDRXEBojKbTgnUg7NDS") + } + + // ------------------------------------------------------------------------ + // Instructions + // ------------------------------------------------------------------------ + + // .................................................................... + // Instruction: InitializePerformancePackage + // .................................................................... + + /// Main instruction struct for InitializePerformancePackage + pub struct InitializePerformancePackageInstruction { + pub accounts: InitializePerformancePackageInstructionAccountMetas, + pub data: InitializePerformancePackageInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializePerformancePackage instruction + #[derive(Debug, Clone, Default)] + pub struct InitializePerformancePackageInstructionAccountMetas { + pub performancePackage: AccountMeta, + + pub createKey: AccountMeta, + + pub tokenMint: AccountMeta, + + pub grantorTokenAccount: AccountMeta, + + pub grantor: AccountMeta, + + pub performancePackageTokenVault: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializePerformancePackage instruction + #[derive(Debug, Clone)] + pub struct InitializePerformancePackageInstructionAccounts { + pub performancePackage: Pubkey, + + pub createKey: Pubkey, + + pub tokenMint: Pubkey, + + pub grantorTokenAccount: Pubkey, + + pub grantor: Pubkey, + + pub performancePackageTokenVault: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializePerformancePackageInstructionAccounts { + pub fn new( + performancePackage: Pubkey, + + createKey: Pubkey, + + tokenMint: Pubkey, + + grantorTokenAccount: Pubkey, + + grantor: Pubkey, + + performancePackageTokenVault: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + performancePackage, + + createKey, + + tokenMint, + + grantorTokenAccount, + + grantor, + + performancePackageTokenVault, + + payer, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializePerformancePackage + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializePerformancePackageInstructionData { + pub params: InitializePerformancePackageParams, + } + + impl InitializePerformancePackageInstructionData { + pub fn new(params: InitializePerformancePackageParams) -> Self { + Self { params } + } + } + + /// Implementation for InitializePerformancePackageInstruction + impl InitializePerformancePackageInstruction { + fn discriminator() -> [u8; 8] { + [3u8, 171u8, 56u8, 55u8, 135u8, 100u8, 163u8, 186u8] + } + + pub fn data(data: InitializePerformancePackageInstructionData) -> Self { + Self { + accounts: InitializePerformancePackageInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: InitializePerformancePackageInstructionAccounts, + ) -> Self { + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.createKey = AccountMeta::new_readonly(accounts.createKey, true); + + self.accounts.tokenMint = AccountMeta::new_readonly(accounts.tokenMint, false); + + self.accounts.grantorTokenAccount = + AccountMeta::new(accounts.grantorTokenAccount, false); + + self.accounts.grantor = AccountMeta::new_readonly(accounts.grantor, true); + + self.accounts.performancePackageTokenVault = + AccountMeta::new(accounts.performancePackageTokenVault, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.createKey.clone()); + + metas.push(self.accounts.tokenMint.clone()); + + metas.push(self.accounts.grantorTokenAccount.clone()); + + metas.push(self.accounts.grantor.clone()); + + metas.push(self.accounts.performancePackageTokenVault.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: StartUnlock + // .................................................................... + + /// Main instruction struct for StartUnlock + pub struct StartUnlockInstruction { + pub accounts: StartUnlockInstructionAccountMetas, + pub data: StartUnlockInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for StartUnlock instruction + #[derive(Debug, Clone, Default)] + pub struct StartUnlockInstructionAccountMetas { + pub performancePackage: AccountMeta, + + pub oracleAccount: AccountMeta, + + pub recipient: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for StartUnlock instruction + #[derive(Debug, Clone)] + pub struct StartUnlockInstructionAccounts { + pub performancePackage: Pubkey, + + pub oracleAccount: Pubkey, + + pub recipient: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl StartUnlockInstructionAccounts { + pub fn new( + performancePackage: Pubkey, + + oracleAccount: Pubkey, + + recipient: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + performancePackage, + + oracleAccount, + + recipient, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for StartUnlock + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct StartUnlockInstructionData {} + + impl StartUnlockInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for StartUnlockInstruction + impl StartUnlockInstruction { + fn discriminator() -> [u8; 8] { + [53u8, 202u8, 212u8, 31u8, 76u8, 178u8, 31u8, 123u8] + } + + pub fn data(data: StartUnlockInstructionData) -> Self { + Self { + accounts: StartUnlockInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: StartUnlockInstructionAccounts) -> Self { + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.oracleAccount = AccountMeta::new_readonly(accounts.oracleAccount, false); + + self.accounts.recipient = AccountMeta::new_readonly(accounts.recipient, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.oracleAccount.clone()); + + metas.push(self.accounts.recipient.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CompleteUnlock + // .................................................................... + + /// Main instruction struct for CompleteUnlock + pub struct CompleteUnlockInstruction { + pub accounts: CompleteUnlockInstructionAccountMetas, + pub data: CompleteUnlockInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CompleteUnlock instruction + #[derive(Debug, Clone, Default)] + pub struct CompleteUnlockInstructionAccountMetas { + pub performancePackage: AccountMeta, + + pub oracleAccount: AccountMeta, + + pub performancePackageTokenVault: AccountMeta, + + pub tokenMint: AccountMeta, + + pub recipientTokenAccount: AccountMeta, + + pub tokenRecipient: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CompleteUnlock instruction + #[derive(Debug, Clone)] + pub struct CompleteUnlockInstructionAccounts { + pub performancePackage: Pubkey, + + pub oracleAccount: Pubkey, + + pub performancePackageTokenVault: Pubkey, + + pub tokenMint: Pubkey, + + pub recipientTokenAccount: Pubkey, + + pub tokenRecipient: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CompleteUnlockInstructionAccounts { + pub fn new( + performancePackage: Pubkey, + + oracleAccount: Pubkey, + + performancePackageTokenVault: Pubkey, + + tokenMint: Pubkey, + + recipientTokenAccount: Pubkey, + + tokenRecipient: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + performancePackage, + + oracleAccount, + + performancePackageTokenVault, + + tokenMint, + + recipientTokenAccount, + + tokenRecipient, + + payer, + + systemProgram, + + tokenProgram, + + associatedTokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CompleteUnlock + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CompleteUnlockInstructionData {} + + impl CompleteUnlockInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CompleteUnlockInstruction + impl CompleteUnlockInstruction { + fn discriminator() -> [u8; 8] { + [22u8, 203u8, 15u8, 189u8, 239u8, 125u8, 25u8, 132u8] + } + + pub fn data(data: CompleteUnlockInstructionData) -> Self { + Self { + accounts: CompleteUnlockInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CompleteUnlockInstructionAccounts) -> Self { + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.oracleAccount = AccountMeta::new_readonly(accounts.oracleAccount, false); + + self.accounts.performancePackageTokenVault = + AccountMeta::new(accounts.performancePackageTokenVault, false); + + self.accounts.tokenMint = AccountMeta::new_readonly(accounts.tokenMint, false); + + self.accounts.recipientTokenAccount = + AccountMeta::new(accounts.recipientTokenAccount, false); + + self.accounts.tokenRecipient = + AccountMeta::new_readonly(accounts.tokenRecipient, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.oracleAccount.clone()); + + metas.push(self.accounts.performancePackageTokenVault.clone()); + + metas.push(self.accounts.tokenMint.clone()); + + metas.push(self.accounts.recipientTokenAccount.clone()); + + metas.push(self.accounts.tokenRecipient.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ProposeChange + // .................................................................... + + /// Main instruction struct for ProposeChange + pub struct ProposeChangeInstruction { + pub accounts: ProposeChangeInstructionAccountMetas, + pub data: ProposeChangeInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ProposeChange instruction + #[derive(Debug, Clone, Default)] + pub struct ProposeChangeInstructionAccountMetas { + pub changeRequest: AccountMeta, + + pub performancePackage: AccountMeta, + + pub proposer: AccountMeta, + + pub payer: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ProposeChange instruction + #[derive(Debug, Clone)] + pub struct ProposeChangeInstructionAccounts { + pub changeRequest: Pubkey, + + pub performancePackage: Pubkey, + + pub proposer: Pubkey, + + pub payer: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ProposeChangeInstructionAccounts { + pub fn new( + changeRequest: Pubkey, + + performancePackage: Pubkey, + + proposer: Pubkey, + + payer: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + changeRequest, + + performancePackage, + + proposer, + + payer, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ProposeChange + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ProposeChangeInstructionData { + pub params: ProposeChangeParams, + } + + impl ProposeChangeInstructionData { + pub fn new(params: ProposeChangeParams) -> Self { + Self { params } + } + } + + /// Implementation for ProposeChangeInstruction + impl ProposeChangeInstruction { + fn discriminator() -> [u8; 8] { + [167u8, 211u8, 18u8, 222u8, 93u8, 215u8, 74u8, 159u8] + } + + pub fn data(data: ProposeChangeInstructionData) -> Self { + Self { + accounts: ProposeChangeInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ProposeChangeInstructionAccounts) -> Self { + self.accounts.changeRequest = AccountMeta::new(accounts.changeRequest, false); + + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.proposer = AccountMeta::new_readonly(accounts.proposer, true); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.changeRequest.clone()); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.proposer.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ExecuteChange + // .................................................................... + + /// Main instruction struct for ExecuteChange + pub struct ExecuteChangeInstruction { + pub accounts: ExecuteChangeInstructionAccountMetas, + pub data: ExecuteChangeInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ExecuteChange instruction + #[derive(Debug, Clone, Default)] + pub struct ExecuteChangeInstructionAccountMetas { + pub changeRequest: AccountMeta, + + pub performancePackage: AccountMeta, + + pub executor: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ExecuteChange instruction + #[derive(Debug, Clone)] + pub struct ExecuteChangeInstructionAccounts { + pub changeRequest: Pubkey, + + pub performancePackage: Pubkey, + + pub executor: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ExecuteChangeInstructionAccounts { + pub fn new( + changeRequest: Pubkey, + + performancePackage: Pubkey, + + executor: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + changeRequest, + + performancePackage, + + executor, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ExecuteChange + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ExecuteChangeInstructionData {} + + impl ExecuteChangeInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for ExecuteChangeInstruction + impl ExecuteChangeInstruction { + fn discriminator() -> [u8; 8] { + [104u8, 53u8, 136u8, 238u8, 82u8, 222u8, 200u8, 42u8] + } + + pub fn data(data: ExecuteChangeInstructionData) -> Self { + Self { + accounts: ExecuteChangeInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ExecuteChangeInstructionAccounts) -> Self { + self.accounts.changeRequest = AccountMeta::new(accounts.changeRequest, false); + + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.executor = AccountMeta::new(accounts.executor, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.changeRequest.clone()); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.executor.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: ChangePerformancePackageAuthority + // .................................................................... + + /// Main instruction struct for ChangePerformancePackageAuthority + pub struct ChangePerformancePackageAuthorityInstruction { + pub accounts: ChangePerformancePackageAuthorityInstructionAccountMetas, + pub data: ChangePerformancePackageAuthorityInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for ChangePerformancePackageAuthority instruction + #[derive(Debug, Clone, Default)] + pub struct ChangePerformancePackageAuthorityInstructionAccountMetas { + pub performancePackage: AccountMeta, + + pub currentAuthority: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for ChangePerformancePackageAuthority instruction + #[derive(Debug, Clone)] + pub struct ChangePerformancePackageAuthorityInstructionAccounts { + pub performancePackage: Pubkey, + + pub currentAuthority: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl ChangePerformancePackageAuthorityInstructionAccounts { + pub fn new( + performancePackage: Pubkey, + + currentAuthority: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + performancePackage, + + currentAuthority, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for ChangePerformancePackageAuthority + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct ChangePerformancePackageAuthorityInstructionData { + pub params: ChangePerformancePackageAuthorityParams, + } + + impl ChangePerformancePackageAuthorityInstructionData { + pub fn new(params: ChangePerformancePackageAuthorityParams) -> Self { + Self { params } + } + } + + /// Implementation for ChangePerformancePackageAuthorityInstruction + impl ChangePerformancePackageAuthorityInstruction { + fn discriminator() -> [u8; 8] { + [167u8, 96u8, 117u8, 32u8, 209u8, 186u8, 226u8, 179u8] + } + + pub fn data(data: ChangePerformancePackageAuthorityInstructionData) -> Self { + Self { + accounts: ChangePerformancePackageAuthorityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: ChangePerformancePackageAuthorityInstructionAccounts, + ) -> Self { + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.currentAuthority = + AccountMeta::new_readonly(accounts.currentAuthority, true); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.currentAuthority.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: BurnPerformancePackage + // .................................................................... + + /// Main instruction struct for BurnPerformancePackage + pub struct BurnPerformancePackageInstruction { + pub accounts: BurnPerformancePackageInstructionAccountMetas, + pub data: BurnPerformancePackageInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for BurnPerformancePackage instruction + #[derive(Debug, Clone, Default)] + pub struct BurnPerformancePackageInstructionAccountMetas { + pub performancePackage: AccountMeta, + + pub performancePackageTokenVault: AccountMeta, + + pub admin: AccountMeta, + + pub spillAccount: AccountMeta, + + pub tokenMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for BurnPerformancePackage instruction + #[derive(Debug, Clone)] + pub struct BurnPerformancePackageInstructionAccounts { + pub performancePackage: Pubkey, + + pub performancePackageTokenVault: Pubkey, + + pub admin: Pubkey, + + pub spillAccount: Pubkey, + + pub tokenMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl BurnPerformancePackageInstructionAccounts { + pub fn new( + performancePackage: Pubkey, + + performancePackageTokenVault: Pubkey, + + admin: Pubkey, + + spillAccount: Pubkey, + + tokenMint: Pubkey, + + tokenProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + performancePackage, + + performancePackageTokenVault, + + admin, + + spillAccount, + + tokenMint, + + tokenProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for BurnPerformancePackage + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct BurnPerformancePackageInstructionData {} + + impl BurnPerformancePackageInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for BurnPerformancePackageInstruction + impl BurnPerformancePackageInstruction { + fn discriminator() -> [u8; 8] { + [232u8, 251u8, 10u8, 122u8, 204u8, 255u8, 248u8, 208u8] + } + + pub fn data(data: BurnPerformancePackageInstructionData) -> Self { + Self { + accounts: BurnPerformancePackageInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: BurnPerformancePackageInstructionAccounts) -> Self { + self.accounts.performancePackage = AccountMeta::new(accounts.performancePackage, false); + + self.accounts.performancePackageTokenVault = + AccountMeta::new(accounts.performancePackageTokenVault, false); + + self.accounts.admin = AccountMeta::new(accounts.admin, true); + + self.accounts.spillAccount = AccountMeta::new(accounts.spillAccount, false); + + self.accounts.tokenMint = AccountMeta::new(accounts.tokenMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.performancePackage.clone()); + + metas.push(self.accounts.performancePackageTokenVault.clone()); + + metas.push(self.accounts.admin.clone()); + + metas.push(self.accounts.spillAccount.clone()); + + metas.push(self.accounts.tokenMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // ------------------------------------------------------------------------ + // Data Accounts (with discriminators) + // ------------------------------------------------------------------------ + + /// AccountDiscriminator implementation for PerformancePackage + impl AccountDiscriminator for PerformancePackage { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + /// AccountDiscriminator implementation for ChangeRequest + impl AccountDiscriminator for ChangeRequest { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + // ------------------------------------------------------------------------ + // Errors + // ------------------------------------------------------------------------ + + /// Program errors for price_based_performance_package + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum PricebasedperformancepackageError { + /// Unlock timestamp has not been reached yet + UnlockTimestampNotReached = 6000, + + /// Unlock timestamp must be in the future + UnlockTimestampInThePast = 6001, + + /// Performance package is not in the expected state + InvalidPerformancePackageState = 6002, + + /// TWAP calculation failed + TwapPeriodNotElapsed = 6003, + + /// Price threshold not met + PriceThresholdNotMet = 6004, + + /// Invalid oracle account data + InvalidOracleData = 6005, + + /// Unauthorized to create or execute change request + UnauthorizedChangeRequest = 6006, + + /// Change request does not match locker + InvalidChangeRequest = 6007, + + /// Unauthorized locker authority + UnauthorizedLockerAuthority = 6008, + + /// An invariant was violated. You should get in contact with the + /// MetaDAO team if you see this + InvariantViolated = 6009, + + /// Tranche price thresholds must be monotonically increasing + TranchePriceThresholdsNotMonotonic = 6010, + + /// Tranche token amount must be greater than 0 + TrancheTokenAmountZero = 6011, + + /// TWAP length must be greater than or equal to 1 day and less than 1 + /// year + InvalidTwapLength = 6012, + + /// Invalid admin + InvalidAdmin = 6013, + + /// Total token amount calculation would overflow + TotalTokenAmountOverflow = 6014, + } + + impl PricebasedperformancepackageError { + /// Get the error code + pub fn code(&self) -> u32 { + *self as u32 + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + match self { + + Self::UnlockTimestampNotReached => "Unlock timestamp has not been reached yet", + + Self::UnlockTimestampInThePast => "Unlock timestamp must be in the future", + + Self::InvalidPerformancePackageState => "Performance package is not in the expected state", + + Self::TwapPeriodNotElapsed => "TWAP calculation failed", + + Self::PriceThresholdNotMet => "Price threshold not met", + + Self::InvalidOracleData => "Invalid oracle account data", + + Self::UnauthorizedChangeRequest => "Unauthorized to create or execute change request", + + Self::InvalidChangeRequest => "Change request does not match locker", + + Self::UnauthorizedLockerAuthority => "Unauthorized locker authority", + + Self::InvariantViolated => "An invariant was violated. You should get in contact with the MetaDAO team if you see this", + + Self::TranchePriceThresholdsNotMonotonic => "Tranche price thresholds must be monotonically increasing", + + Self::TrancheTokenAmountZero => "Tranche token amount must be greater than 0", + + Self::InvalidTwapLength => "TWAP length must be greater than or equal to 1 day and less than 1 year", + + Self::InvalidAdmin => "Invalid admin", + + Self::TotalTokenAmountOverflow => "Total token amount calculation would overflow", + + } + } + + /// Try to convert from error code + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::UnlockTimestampNotReached), + + 6001 => Some(Self::UnlockTimestampInThePast), + + 6002 => Some(Self::InvalidPerformancePackageState), + + 6003 => Some(Self::TwapPeriodNotElapsed), + + 6004 => Some(Self::PriceThresholdNotMet), + + 6005 => Some(Self::InvalidOracleData), + + 6006 => Some(Self::UnauthorizedChangeRequest), + + 6007 => Some(Self::InvalidChangeRequest), + + 6008 => Some(Self::UnauthorizedLockerAuthority), + + 6009 => Some(Self::InvariantViolated), + + 6010 => Some(Self::TranchePriceThresholdsNotMonotonic), + + 6011 => Some(Self::TrancheTokenAmountZero), + + 6012 => Some(Self::InvalidTwapLength), + + 6013 => Some(Self::InvalidAdmin), + + 6014 => Some(Self::TotalTokenAmountOverflow), + + _ => None, + } + } + } + + impl std::fmt::Display for PricebasedperformancepackageError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.code(), self.msg()) + } + } + + impl std::error::Error for PricebasedperformancepackageError {} + + // ------------------------------------------------------------------------ + // Composite Accounts + // ------------------------------------------------------------------------ + + // ------------------------------------------------------------------------ + // Custom Types + // ------------------------------------------------------------------------ + + /// Custom struct: CommonFields + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct CommonFields { + pub slot: u64, + + pub unixTimestamp: i64, + + pub performancePackageSeqNum: u64, + } + + impl CommonFields { + pub fn new(slot: u64, unixTimestamp: i64, performancePackageSeqNum: u64) -> Self { + Self { + slot, + + unixTimestamp, + + performancePackageSeqNum, + } + } + } + + /// Custom struct: ChangePerformancePackageAuthorityParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ChangePerformancePackageAuthorityParams { + pub newPerformancePackageAuthority: Pubkey, + } + + impl ChangePerformancePackageAuthorityParams { + pub fn new(newPerformancePackageAuthority: Pubkey) -> Self { + Self { + newPerformancePackageAuthority, + } + } + } + + /// Custom struct: InitializePerformancePackageParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitializePerformancePackageParams { + pub tranches: Vec, + + pub minUnlockTimestamp: i64, + + pub oracleConfig: OracleConfig, + + pub twapLengthSeconds: u32, + + pub grantee: Pubkey, + + pub performancePackageAuthority: Pubkey, + } + + impl InitializePerformancePackageParams { + pub fn new( + tranches: Vec, + + minUnlockTimestamp: i64, + + oracleConfig: OracleConfig, + + twapLengthSeconds: u32, + + grantee: Pubkey, + + performancePackageAuthority: Pubkey, + ) -> Self { + Self { + tranches, + + minUnlockTimestamp, + + oracleConfig, + + twapLengthSeconds, + + grantee, + + performancePackageAuthority, + } + } + } + + /// Custom struct: ProposeChangeParams + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ProposeChangeParams { + pub changeType: ChangeType, + + pub pdaNonce: u32, + } + + impl ProposeChangeParams { + pub fn new(changeType: ChangeType, pdaNonce: u32) -> Self { + Self { + changeType, + + pdaNonce, + } + } + } + + /// Custom struct: OracleConfig + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct OracleConfig { + pub oracleAccount: Pubkey, + + pub byteOffset: u32, + } + + impl OracleConfig { + pub fn new(oracleAccount: Pubkey, byteOffset: u32) -> Self { + Self { + oracleAccount, + + byteOffset, + } + } + } + + /// Custom struct: Tranche + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct Tranche { + pub priceThreshold: u128, + + pub tokenAmount: u64, + } + + impl Tranche { + pub fn new(priceThreshold: u128, tokenAmount: u64) -> Self { + Self { + priceThreshold, + + tokenAmount, + } + } + } + + /// Custom struct: StoredTranche + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct StoredTranche { + pub priceThreshold: u128, + + pub tokenAmount: u64, + + pub isUnlocked: bool, + } + + impl StoredTranche { + pub fn new(priceThreshold: u128, tokenAmount: u64, isUnlocked: bool) -> Self { + Self { + priceThreshold, + + tokenAmount, + + isUnlocked, + } + } + } + + /// Custom enum: PerformancePackageState + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum PerformancePackageState { + Locked, + + Unlocking { + startAggregator: u128, + + startTimestamp: i64, + }, + } + + /// Custom enum: ChangeType + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum ChangeType { + Oracle { newOracleConfig: OracleConfig }, + + Recipient { newRecipient: Pubkey }, + } + + /// Custom enum: ProposerType + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub enum ProposerType { + Recipient, + + Authority, + } + + /// Custom struct: PerformancePackage + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct PerformancePackage { + pub tranches: Vec, + + pub totalTokenAmount: u64, + + pub alreadyUnlockedAmount: u64, + + pub minUnlockTimestamp: i64, + + pub oracleConfig: OracleConfig, + + pub twapLengthSeconds: u32, + + pub recipient: Pubkey, + + pub state: PerformancePackageState, + + pub createKey: Pubkey, + + pub pdaBump: u8, + + pub performancePackageAuthority: Pubkey, + + pub tokenMint: Pubkey, + + pub seqNum: u64, + + pub performancePackageTokenVault: Pubkey, + } + + impl PerformancePackage { + pub fn new( + tranches: Vec, + + totalTokenAmount: u64, + + alreadyUnlockedAmount: u64, + + minUnlockTimestamp: i64, + + oracleConfig: OracleConfig, + + twapLengthSeconds: u32, + + recipient: Pubkey, + + state: PerformancePackageState, + + createKey: Pubkey, + + pdaBump: u8, + + performancePackageAuthority: Pubkey, + + tokenMint: Pubkey, + + seqNum: u64, + + performancePackageTokenVault: Pubkey, + ) -> Self { + Self { + tranches, + + totalTokenAmount, + + alreadyUnlockedAmount, + + minUnlockTimestamp, + + oracleConfig, + + twapLengthSeconds, + + recipient, + + state, + + createKey, + + pdaBump, + + performancePackageAuthority, + + tokenMint, + + seqNum, + + performancePackageTokenVault, + } + } + } + + /// Custom struct: ChangeRequest + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct ChangeRequest { + pub performancePackage: Pubkey, + + pub changeType: ChangeType, + + pub proposedAt: i64, + + pub proposerType: ProposerType, + + pub pdaNonce: u32, + + pub pdaBump: u8, + } + + impl ChangeRequest { + pub fn new( + performancePackage: Pubkey, + + changeType: ChangeType, + + proposedAt: i64, + + proposerType: ProposerType, + + pdaNonce: u32, + + pdaBump: u8, + ) -> Self { + Self { + performancePackage, + + changeType, + + proposedAt, + + proposerType, + + pdaNonce, + + pdaBump, + } + } + } +} + +// ---------------------------------------------------------------------------- +// Program: bid_wall +// ---------------------------------------------------------------------------- +pub mod bid_wall { + use super::*; + + // ------------------------------------------------------------------------ + // Program ID + // ------------------------------------------------------------------------ + + /// Returns the program ID for bid_wall + pub fn program_id() -> Pubkey { + pubkey!("WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx") + } + + // ------------------------------------------------------------------------ + // Instructions + // ------------------------------------------------------------------------ + + // .................................................................... + // Instruction: InitializeBidWall + // .................................................................... + + /// Main instruction struct for InitializeBidWall + pub struct InitializeBidWallInstruction { + pub accounts: InitializeBidWallInstructionAccountMetas, + pub data: InitializeBidWallInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for InitializeBidWall instruction + #[derive(Debug, Clone, Default)] + pub struct InitializeBidWallInstructionAccountMetas { + pub bidWall: AccountMeta, + + pub payer: AccountMeta, + + pub feeRecipient: AccountMeta, + + pub creator: AccountMeta, + + pub authority: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub creatorQuoteTokenAccount: AccountMeta, + + pub daoTreasury: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub associatedTokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for InitializeBidWall instruction + #[derive(Debug, Clone)] + pub struct InitializeBidWallInstructionAccounts { + pub bidWall: Pubkey, + + pub payer: Pubkey, + + pub feeRecipient: Pubkey, + + pub creator: Pubkey, + + pub authority: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub creatorQuoteTokenAccount: Pubkey, + + pub daoTreasury: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub associatedTokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl InitializeBidWallInstructionAccounts { + pub fn new( + bidWall: Pubkey, + + payer: Pubkey, + + feeRecipient: Pubkey, + + creator: Pubkey, + + authority: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + creatorQuoteTokenAccount: Pubkey, + + daoTreasury: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + tokenProgram: Pubkey, + + associatedTokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + bidWall, + + payer, + + feeRecipient, + + creator, + + authority, + + bidWallQuoteTokenAccount, + + creatorQuoteTokenAccount, + + daoTreasury, + + baseMint, + + quoteMint, + + tokenProgram, + + associatedTokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for InitializeBidWall + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct InitializeBidWallInstructionData { + pub args: InitializeBidWallArgs, + } + + impl InitializeBidWallInstructionData { + pub fn new(args: InitializeBidWallArgs) -> Self { + Self { args } + } + } + + /// Implementation for InitializeBidWallInstruction + impl InitializeBidWallInstruction { + fn discriminator() -> [u8; 8] { + [255u8, 36u8, 136u8, 45u8, 188u8, 82u8, 221u8, 218u8] + } + + pub fn data(data: InitializeBidWallInstructionData) -> Self { + Self { + accounts: InitializeBidWallInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeBidWallInstructionAccounts) -> Self { + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.feeRecipient = AccountMeta::new_readonly(accounts.feeRecipient, false); + + self.accounts.creator = AccountMeta::new_readonly(accounts.creator, true); + + self.accounts.authority = AccountMeta::new_readonly(accounts.authority, false); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.creatorQuoteTokenAccount = + AccountMeta::new(accounts.creatorQuoteTokenAccount, false); + + self.accounts.daoTreasury = AccountMeta::new_readonly(accounts.daoTreasury, false); + + self.accounts.baseMint = AccountMeta::new_readonly(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.associatedTokenProgram = + AccountMeta::new_readonly(accounts.associatedTokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.feeRecipient.clone()); + + metas.push(self.accounts.creator.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.creatorQuoteTokenAccount.clone()); + + metas.push(self.accounts.daoTreasury.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.associatedTokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CloseBidWall + // .................................................................... + + /// Main instruction struct for CloseBidWall + pub struct CloseBidWallInstruction { + pub accounts: CloseBidWallInstructionAccountMetas, + pub data: CloseBidWallInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CloseBidWall instruction + #[derive(Debug, Clone, Default)] + pub struct CloseBidWallInstructionAccountMetas { + pub bidWall: AccountMeta, + + pub payer: AccountMeta, + + pub authority: AccountMeta, + + pub feeRecipient: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub authorityQuoteTokenAccount: AccountMeta, + + pub feeRecipientQuoteTokenAccount: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CloseBidWall instruction + #[derive(Debug, Clone)] + pub struct CloseBidWallInstructionAccounts { + pub bidWall: Pubkey, + + pub payer: Pubkey, + + pub authority: Pubkey, + + pub feeRecipient: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub authorityQuoteTokenAccount: Pubkey, + + pub feeRecipientQuoteTokenAccount: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CloseBidWallInstructionAccounts { + pub fn new( + bidWall: Pubkey, + + payer: Pubkey, + + authority: Pubkey, + + feeRecipient: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + authorityQuoteTokenAccount: Pubkey, + + feeRecipientQuoteTokenAccount: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + bidWall, + + payer, + + authority, + + feeRecipient, + + bidWallQuoteTokenAccount, + + authorityQuoteTokenAccount, + + feeRecipientQuoteTokenAccount, + + baseMint, + + quoteMint, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CloseBidWall + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CloseBidWallInstructionData {} + + impl CloseBidWallInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CloseBidWallInstruction + impl CloseBidWallInstruction { + fn discriminator() -> [u8; 8] { + [58u8, 249u8, 140u8, 44u8, 227u8, 80u8, 109u8, 78u8] + } + + pub fn data(data: CloseBidWallInstructionData) -> Self { + Self { + accounts: CloseBidWallInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CloseBidWallInstructionAccounts) -> Self { + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.authority = AccountMeta::new(accounts.authority, false); + + self.accounts.feeRecipient = AccountMeta::new_readonly(accounts.feeRecipient, false); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.authorityQuoteTokenAccount = + AccountMeta::new(accounts.authorityQuoteTokenAccount, false); + + self.accounts.feeRecipientQuoteTokenAccount = + AccountMeta::new(accounts.feeRecipientQuoteTokenAccount, false); + + self.accounts.baseMint = AccountMeta::new_readonly(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.feeRecipient.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.authorityQuoteTokenAccount.clone()); + + metas.push(self.accounts.feeRecipientQuoteTokenAccount.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: SellTokens + // .................................................................... + + /// Main instruction struct for SellTokens + pub struct SellTokensInstruction { + pub accounts: SellTokensInstructionAccountMetas, + pub data: SellTokensInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for SellTokens instruction + #[derive(Debug, Clone, Default)] + pub struct SellTokensInstructionAccountMetas { + pub bidWall: AccountMeta, + + pub user: AccountMeta, + + pub userTokenAccount: AccountMeta, + + pub userQuoteTokenAccount: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub daoTreasury: AccountMeta, + + pub daoTreasuryQuoteTokenAccount: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for SellTokens instruction + #[derive(Debug, Clone)] + pub struct SellTokensInstructionAccounts { + pub bidWall: Pubkey, + + pub user: Pubkey, + + pub userTokenAccount: Pubkey, + + pub userQuoteTokenAccount: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub daoTreasury: Pubkey, + + pub daoTreasuryQuoteTokenAccount: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl SellTokensInstructionAccounts { + pub fn new( + bidWall: Pubkey, + + user: Pubkey, + + userTokenAccount: Pubkey, + + userQuoteTokenAccount: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + daoTreasury: Pubkey, + + daoTreasuryQuoteTokenAccount: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + bidWall, + + user, + + userTokenAccount, + + userQuoteTokenAccount, + + bidWallQuoteTokenAccount, + + daoTreasury, + + daoTreasuryQuoteTokenAccount, + + baseMint, + + quoteMint, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for SellTokens + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct SellTokensInstructionData { + pub args: SellTokensArgs, + } + + impl SellTokensInstructionData { + pub fn new(args: SellTokensArgs) -> Self { + Self { args } + } + } + + /// Implementation for SellTokensInstruction + impl SellTokensInstruction { + fn discriminator() -> [u8; 8] { + [114u8, 242u8, 25u8, 12u8, 62u8, 126u8, 92u8, 2u8] + } + + pub fn data(data: SellTokensInstructionData) -> Self { + Self { + accounts: SellTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: SellTokensInstructionAccounts) -> Self { + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.user = AccountMeta::new(accounts.user, true); + + self.accounts.userTokenAccount = AccountMeta::new(accounts.userTokenAccount, false); + + self.accounts.userQuoteTokenAccount = + AccountMeta::new(accounts.userQuoteTokenAccount, false); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.daoTreasury = AccountMeta::new_readonly(accounts.daoTreasury, false); + + self.accounts.daoTreasuryQuoteTokenAccount = + AccountMeta::new_readonly(accounts.daoTreasuryQuoteTokenAccount, false); + + self.accounts.baseMint = AccountMeta::new(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.user.clone()); + + metas.push(self.accounts.userTokenAccount.clone()); + + metas.push(self.accounts.userQuoteTokenAccount.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.daoTreasury.clone()); + + metas.push(self.accounts.daoTreasuryQuoteTokenAccount.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CollectFees + // .................................................................... + + /// Main instruction struct for CollectFees + pub struct CollectFeesInstruction { + pub accounts: CollectFeesInstructionAccountMetas, + pub data: CollectFeesInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CollectFees instruction + #[derive(Debug, Clone, Default)] + pub struct CollectFeesInstructionAccountMetas { + pub bidWall: AccountMeta, + + pub cranker: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub feeRecipientQuoteTokenAccount: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CollectFees instruction + #[derive(Debug, Clone)] + pub struct CollectFeesInstructionAccounts { + pub bidWall: Pubkey, + + pub cranker: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub feeRecipientQuoteTokenAccount: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CollectFeesInstructionAccounts { + pub fn new( + bidWall: Pubkey, + + cranker: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + feeRecipientQuoteTokenAccount: Pubkey, + + quoteMint: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + bidWall, + + cranker, + + bidWallQuoteTokenAccount, + + feeRecipientQuoteTokenAccount, + + quoteMint, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CollectFees + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CollectFeesInstructionData {} + + impl CollectFeesInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CollectFeesInstruction + impl CollectFeesInstruction { + fn discriminator() -> [u8; 8] { + [164u8, 152u8, 207u8, 99u8, 30u8, 186u8, 19u8, 182u8] + } + + pub fn data(data: CollectFeesInstructionData) -> Self { + Self { + accounts: CollectFeesInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CollectFeesInstructionAccounts) -> Self { + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.cranker = AccountMeta::new_readonly(accounts.cranker, true); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.feeRecipientQuoteTokenAccount = + AccountMeta::new(accounts.feeRecipientQuoteTokenAccount, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.cranker.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.feeRecipientQuoteTokenAccount.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // .................................................................... + // Instruction: CancelBidWall + // .................................................................... + + /// Main instruction struct for CancelBidWall + pub struct CancelBidWallInstruction { + pub accounts: CancelBidWallInstructionAccountMetas, + pub data: CancelBidWallInstructionData, + pub remaining_accounts: Vec, + } + + /// Account metadata for CancelBidWall instruction + #[derive(Debug, Clone, Default)] + pub struct CancelBidWallInstructionAccountMetas { + pub bidWall: AccountMeta, + + pub payer: AccountMeta, + + pub authority: AccountMeta, + + pub feeRecipient: AccountMeta, + + pub bidWallQuoteTokenAccount: AccountMeta, + + pub authorityQuoteTokenAccount: AccountMeta, + + pub feeRecipientQuoteTokenAccount: AccountMeta, + + pub baseMint: AccountMeta, + + pub quoteMint: AccountMeta, + + pub tokenProgram: AccountMeta, + + pub systemProgram: AccountMeta, + + pub eventAuthority: AccountMeta, + + pub program: AccountMeta, + } + + /// Account pubkeys for CancelBidWall instruction + #[derive(Debug, Clone)] + pub struct CancelBidWallInstructionAccounts { + pub bidWall: Pubkey, + + pub payer: Pubkey, + + pub authority: Pubkey, + + pub feeRecipient: Pubkey, + + pub bidWallQuoteTokenAccount: Pubkey, + + pub authorityQuoteTokenAccount: Pubkey, + + pub feeRecipientQuoteTokenAccount: Pubkey, + + pub baseMint: Pubkey, + + pub quoteMint: Pubkey, + + pub tokenProgram: Pubkey, + + pub systemProgram: Pubkey, + + pub eventAuthority: Pubkey, + + pub program: Pubkey, + } + + impl CancelBidWallInstructionAccounts { + pub fn new( + bidWall: Pubkey, + + payer: Pubkey, + + authority: Pubkey, + + feeRecipient: Pubkey, + + bidWallQuoteTokenAccount: Pubkey, + + authorityQuoteTokenAccount: Pubkey, + + feeRecipientQuoteTokenAccount: Pubkey, + + baseMint: Pubkey, + + quoteMint: Pubkey, + + tokenProgram: Pubkey, + + systemProgram: Pubkey, + + eventAuthority: Pubkey, + + program: Pubkey, + ) -> Self { + Self { + bidWall, + + payer, + + authority, + + feeRecipient, + + bidWallQuoteTokenAccount, + + authorityQuoteTokenAccount, + + feeRecipientQuoteTokenAccount, + + baseMint, + + quoteMint, + + tokenProgram, + + systemProgram, + + eventAuthority, + + program, + } + } + } + + /// Instruction data for CancelBidWall + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct CancelBidWallInstructionData {} + + impl CancelBidWallInstructionData { + pub fn new() -> Self { + Self {} + } + } + + /// Implementation for CancelBidWallInstruction + impl CancelBidWallInstruction { + fn discriminator() -> [u8; 8] { + [61u8, 200u8, 60u8, 115u8, 213u8, 88u8, 115u8, 138u8] + } + + pub fn data(data: CancelBidWallInstructionData) -> Self { + Self { + accounts: CancelBidWallInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: CancelBidWallInstructionAccounts) -> Self { + self.accounts.bidWall = AccountMeta::new(accounts.bidWall, false); + + self.accounts.payer = AccountMeta::new(accounts.payer, true); + + self.accounts.authority = AccountMeta::new_readonly(accounts.authority, true); + + self.accounts.feeRecipient = AccountMeta::new_readonly(accounts.feeRecipient, false); + + self.accounts.bidWallQuoteTokenAccount = + AccountMeta::new(accounts.bidWallQuoteTokenAccount, false); + + self.accounts.authorityQuoteTokenAccount = + AccountMeta::new(accounts.authorityQuoteTokenAccount, false); + + self.accounts.feeRecipientQuoteTokenAccount = + AccountMeta::new(accounts.feeRecipientQuoteTokenAccount, false); + + self.accounts.baseMint = AccountMeta::new_readonly(accounts.baseMint, false); + + self.accounts.quoteMint = AccountMeta::new_readonly(accounts.quoteMint, false); + + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + + self + } + + pub fn remaining_accounts(mut self, accounts: Vec) -> Self { + self.remaining_accounts = accounts; + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = Vec::new(); + + metas.push(self.accounts.bidWall.clone()); + + metas.push(self.accounts.payer.clone()); + + metas.push(self.accounts.authority.clone()); + + metas.push(self.accounts.feeRecipient.clone()); + + metas.push(self.accounts.bidWallQuoteTokenAccount.clone()); + + metas.push(self.accounts.authorityQuoteTokenAccount.clone()); + + metas.push(self.accounts.feeRecipientQuoteTokenAccount.clone()); + + metas.push(self.accounts.baseMint.clone()); + + metas.push(self.accounts.quoteMint.clone()); + + metas.push(self.accounts.tokenProgram.clone()); + + metas.push(self.accounts.systemProgram.clone()); + + metas.push(self.accounts.eventAuthority.clone()); + + metas.push(self.accounts.program.clone()); + + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + let mut buffer: Vec = Vec::new(); + + buffer.extend_from_slice(&Self::discriminator()); + + self.data.serialize(&mut buffer).unwrap(); + + Instruction::new_with_bytes(program_id(), &buffer, self.to_account_metas()) + } + } + + // ------------------------------------------------------------------------ + // Data Accounts (with discriminators) + // ------------------------------------------------------------------------ + + /// AccountDiscriminator implementation for BidWall + impl AccountDiscriminator for BidWall { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + // ------------------------------------------------------------------------ + // Errors + // ------------------------------------------------------------------------ + + /// Program errors for bid_wall + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum BidwallError { + /// Bid wall expired + BidWallExpired = 6000, + + /// Bid wall not expired + BidWallNotExpired = 6001, + + /// Fee recipient mismatch + FeeRecipientMismatch = 6002, + + /// Insufficient quote reserves + InsufficientQuoteReserves = 6003, + + /// Bid wall depleted + BidWallDepleted = 6004, + + /// Invalid input amount + InvalidInputAmount = 6005, + + /// Invalid crank address + InvalidCrankAddress = 6006, + + /// Insufficient output amount + InsufficientOutputAmount = 6007, + } + + impl BidwallError { + /// Get the error code + pub fn code(&self) -> u32 { + *self as u32 + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + match self { + Self::BidWallExpired => "Bid wall expired", + + Self::BidWallNotExpired => "Bid wall not expired", + + Self::FeeRecipientMismatch => "Fee recipient mismatch", + + Self::InsufficientQuoteReserves => "Insufficient quote reserves", + + Self::BidWallDepleted => "Bid wall depleted", + + Self::InvalidInputAmount => "Invalid input amount", + + Self::InvalidCrankAddress => "Invalid crank address", + + Self::InsufficientOutputAmount => "Insufficient output amount", + } + } + + /// Try to convert from error code + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::BidWallExpired), + + 6001 => Some(Self::BidWallNotExpired), + + 6002 => Some(Self::FeeRecipientMismatch), + + 6003 => Some(Self::InsufficientQuoteReserves), + + 6004 => Some(Self::BidWallDepleted), + + 6005 => Some(Self::InvalidInputAmount), + + 6006 => Some(Self::InvalidCrankAddress), + + 6007 => Some(Self::InsufficientOutputAmount), + + _ => None, + } + } + } + + impl std::fmt::Display for BidwallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.code(), self.msg()) + } + } + + impl std::error::Error for BidwallError {} + + // ------------------------------------------------------------------------ + // Composite Accounts + // ------------------------------------------------------------------------ + + // ------------------------------------------------------------------------ + // Custom Types + // ------------------------------------------------------------------------ + + /// Custom struct: CommonFields + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct CommonFields { + pub slot: u64, + + pub unixTimestamp: i64, + + pub bidWallSeqNum: u64, + } + + impl CommonFields { + pub fn new(slot: u64, unixTimestamp: i64, bidWallSeqNum: u64) -> Self { + Self { + slot, + + unixTimestamp, + + bidWallSeqNum, + } + } + } + + /// Custom struct: InitializeBidWallArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct InitializeBidWallArgs { + pub amount: u64, + + pub nonce: u64, + + pub initialAmmQuoteReserves: u64, + + pub durationSeconds: u32, + } + + impl InitializeBidWallArgs { + pub fn new( + amount: u64, + + nonce: u64, + + initialAmmQuoteReserves: u64, + + durationSeconds: u32, + ) -> Self { + Self { + amount, + + nonce, + + initialAmmQuoteReserves, + + durationSeconds, + } + } + } + + /// Custom struct: SellTokensArgs + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct SellTokensArgs { + pub amountIn: u64, + + pub minAmountOut: u64, + } + + impl SellTokensArgs { + pub fn new(amountIn: u64, minAmountOut: u64) -> Self { + Self { + amountIn, + + minAmountOut, + } + } + } + + /// Custom struct: BidWall + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct BidWall { + pub nonce: u64, + + pub createdTimestamp: i64, + + pub initialAmmQuoteReserves: u64, + + pub quoteAmount: u64, + + pub feesCollected: u64, + + pub baseBoughtAmount: u64, + + pub seqNum: u64, + + pub creator: Pubkey, + + pub authority: Pubkey, + + pub daoTreasury: Pubkey, + + pub baseMint: Pubkey, + + pub feeRecipient: Pubkey, + + pub durationSeconds: u32, + + pub pdaBump: u8, + } + + impl BidWall { + pub fn new( + nonce: u64, + + createdTimestamp: i64, + + initialAmmQuoteReserves: u64, + + quoteAmount: u64, + + feesCollected: u64, + + baseBoughtAmount: u64, + + seqNum: u64, + + creator: Pubkey, + + authority: Pubkey, + + daoTreasury: Pubkey, + + baseMint: Pubkey, + + feeRecipient: Pubkey, + + durationSeconds: u32, + + pdaBump: u8, + ) -> Self { + Self { + nonce, + + createdTimestamp, + + initialAmmQuoteReserves, + + quoteAmount, + + feesCollected, + + baseBoughtAmount, + + seqNum, + + creator, + + authority, + + daoTreasury, + + baseMint, + + feeRecipient, + + durationSeconds, + + pdaBump, + } + } + } +} + +// ---------------------------------------------------------------------------- +// Program: mint_governor +// ---------------------------------------------------------------------------- +pub mod mint_governor { + use super::*; + + /// Returns the program ID for mint_governor + pub fn program_id() -> Pubkey { + pubkey!("gvnr27cVeyW3AVf3acL7VCJ5WjGAphytnsgcK1feHyH") + } + + fn ix_data(discriminator: [u8; 8], data: &T) -> Vec { + let mut out = discriminator.to_vec(); + out.extend_from_slice(&borsh::to_vec(data).expect("instruction data should serialize")); + out + } + + fn ix_data_no_args(discriminator: [u8; 8]) -> Vec { + discriminator.to_vec() + } + + pub struct InitializeMintGovernorInstruction { + pub accounts: InitializeMintGovernorInstructionAccountMetas, + pub data: InitializeMintGovernorInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct InitializeMintGovernorInstructionAccountMetas { + pub mint: AccountMeta, + pub mintGovernor: AccountMeta, + pub createKey: AccountMeta, + pub admin: AccountMeta, + pub payer: AccountMeta, + pub systemProgram: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct InitializeMintGovernorInstructionAccounts { + pub mint: Pubkey, + pub mintGovernor: Pubkey, + pub createKey: Pubkey, + pub admin: Pubkey, + pub payer: Pubkey, + pub systemProgram: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl InitializeMintGovernorInstructionAccounts { + pub fn new( + mint: Pubkey, + mintGovernor: Pubkey, + createKey: Pubkey, + admin: Pubkey, + payer: Pubkey, + systemProgram: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mint, + mintGovernor, + createKey, + admin, + payer, + systemProgram, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, Default)] + pub struct InitializeMintGovernorInstructionData {} + + impl InitializeMintGovernorInstructionData { + pub fn new() -> Self { + Self {} + } + } + + impl InitializeMintGovernorInstruction { + fn discriminator() -> [u8; 8] { + [198, 71, 122, 52, 99, 36, 35, 197] + } + + pub fn data(data: InitializeMintGovernorInstructionData) -> Self { + Self { + accounts: InitializeMintGovernorInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: InitializeMintGovernorInstructionAccounts) -> Self { + self.accounts.mint = AccountMeta::new_readonly(accounts.mint, false); + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.createKey = AccountMeta::new_readonly(accounts.createKey, true); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, false); + self.accounts.payer = AccountMeta::new(accounts.payer, true); + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mint.clone(), + self.accounts.mintGovernor.clone(), + self.accounts.createKey.clone(), + self.accounts.admin.clone(), + self.accounts.payer.clone(), + self.accounts.systemProgram.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data_no_args(Self::discriminator()), + } + } + } + + pub struct TransferAuthorityToGovernorInstruction { + pub accounts: TransferAuthorityToGovernorInstructionAccountMetas, + pub data: TransferAuthorityToGovernorInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct TransferAuthorityToGovernorInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mint: AccountMeta, + pub currentAuthority: AccountMeta, + pub tokenProgram: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct TransferAuthorityToGovernorInstructionAccounts { + pub mintGovernor: Pubkey, + pub mint: Pubkey, + pub currentAuthority: Pubkey, + pub tokenProgram: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl TransferAuthorityToGovernorInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mint: Pubkey, + currentAuthority: Pubkey, + tokenProgram: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mint, + currentAuthority, + tokenProgram, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, Default)] + pub struct TransferAuthorityToGovernorInstructionData {} + + impl TransferAuthorityToGovernorInstructionData { + pub fn new() -> Self { + Self {} + } + } + + impl TransferAuthorityToGovernorInstruction { + fn discriminator() -> [u8; 8] { + [14, 48, 149, 50, 10, 197, 115, 141] + } + + pub fn data(data: TransferAuthorityToGovernorInstructionData) -> Self { + Self { + accounts: TransferAuthorityToGovernorInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts( + mut self, + accounts: TransferAuthorityToGovernorInstructionAccounts, + ) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mint = AccountMeta::new(accounts.mint, false); + self.accounts.currentAuthority = + AccountMeta::new_readonly(accounts.currentAuthority, true); + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mint.clone(), + self.accounts.currentAuthority.clone(), + self.accounts.tokenProgram.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data_no_args(Self::discriminator()), + } + } + } + + pub struct AddMintAuthorityInstruction { + pub accounts: AddMintAuthorityInstructionAccountMetas, + pub data: AddMintAuthorityInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct AddMintAuthorityInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mintAuthority: AccountMeta, + pub admin: AccountMeta, + pub authorizedMinter: AccountMeta, + pub payer: AccountMeta, + pub systemProgram: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct AddMintAuthorityInstructionAccounts { + pub mintGovernor: Pubkey, + pub mintAuthority: Pubkey, + pub admin: Pubkey, + pub authorizedMinter: Pubkey, + pub payer: Pubkey, + pub systemProgram: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl AddMintAuthorityInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mintAuthority: Pubkey, + admin: Pubkey, + authorizedMinter: Pubkey, + payer: Pubkey, + systemProgram: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mintAuthority, + admin, + authorizedMinter, + payer, + systemProgram, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct AddMintAuthorityInstructionData { + pub args: AddMintAuthorityArgs, + } + + impl AddMintAuthorityInstructionData { + pub fn new(args: AddMintAuthorityArgs) -> Self { + Self { args } + } + } + + impl AddMintAuthorityInstruction { + fn discriminator() -> [u8; 8] { + [41, 254, 251, 123, 155, 68, 213, 8] + } + + pub fn data(data: AddMintAuthorityInstructionData) -> Self { + Self { + accounts: AddMintAuthorityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: AddMintAuthorityInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mintAuthority = AccountMeta::new(accounts.mintAuthority, false); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + self.accounts.authorizedMinter = + AccountMeta::new_readonly(accounts.authorizedMinter, false); + self.accounts.payer = AccountMeta::new(accounts.payer, true); + self.accounts.systemProgram = AccountMeta::new_readonly(accounts.systemProgram, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mintAuthority.clone(), + self.accounts.admin.clone(), + self.accounts.authorizedMinter.clone(), + self.accounts.payer.clone(), + self.accounts.systemProgram.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data(Self::discriminator(), &self.data), + } + } + } + + pub struct MintTokensInstruction { + pub accounts: MintTokensInstructionAccountMetas, + pub data: MintTokensInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct MintTokensInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mintAuthority: AccountMeta, + pub mint: AccountMeta, + pub destinationAta: AccountMeta, + pub authorizedMinter: AccountMeta, + pub tokenProgram: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct MintTokensInstructionAccounts { + pub mintGovernor: Pubkey, + pub mintAuthority: Pubkey, + pub mint: Pubkey, + pub destinationAta: Pubkey, + pub authorizedMinter: Pubkey, + pub tokenProgram: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl MintTokensInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mintAuthority: Pubkey, + mint: Pubkey, + destinationAta: Pubkey, + authorizedMinter: Pubkey, + tokenProgram: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mintAuthority, + mint, + destinationAta, + authorizedMinter, + tokenProgram, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct MintTokensInstructionData { + pub args: MintTokensArgs, + } + + impl MintTokensInstructionData { + pub fn new(args: MintTokensArgs) -> Self { + Self { args } + } + } + + impl MintTokensInstruction { + fn discriminator() -> [u8; 8] { + [59, 132, 24, 246, 122, 39, 8, 243] + } + + pub fn data(data: MintTokensInstructionData) -> Self { + Self { + accounts: MintTokensInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: MintTokensInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mintAuthority = AccountMeta::new(accounts.mintAuthority, false); + self.accounts.mint = AccountMeta::new(accounts.mint, false); + self.accounts.destinationAta = AccountMeta::new(accounts.destinationAta, false); + self.accounts.authorizedMinter = + AccountMeta::new_readonly(accounts.authorizedMinter, true); + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mintAuthority.clone(), + self.accounts.mint.clone(), + self.accounts.destinationAta.clone(), + self.accounts.authorizedMinter.clone(), + self.accounts.tokenProgram.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data(Self::discriminator(), &self.data), + } + } + } + + pub struct UpdateMintAuthorityInstruction { + pub accounts: UpdateMintAuthorityInstructionAccountMetas, + pub data: UpdateMintAuthorityInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct UpdateMintAuthorityInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mintAuthority: AccountMeta, + pub admin: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct UpdateMintAuthorityInstructionAccounts { + pub mintGovernor: Pubkey, + pub mintAuthority: Pubkey, + pub admin: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl UpdateMintAuthorityInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mintAuthority: Pubkey, + admin: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mintAuthority, + admin, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] + pub struct UpdateMintAuthorityInstructionData { + pub args: UpdateMintAuthorityArgs, + } + + impl UpdateMintAuthorityInstructionData { + pub fn new(args: UpdateMintAuthorityArgs) -> Self { + Self { args } + } + } + + impl UpdateMintAuthorityInstruction { + fn discriminator() -> [u8; 8] { + [103, 51, 57, 197, 223, 22, 44, 142] + } + + pub fn data(data: UpdateMintAuthorityInstructionData) -> Self { + Self { + accounts: UpdateMintAuthorityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: UpdateMintAuthorityInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mintAuthority = AccountMeta::new(accounts.mintAuthority, false); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mintAuthority.clone(), + self.accounts.admin.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data(Self::discriminator(), &self.data), + } + } + } + + pub struct RemoveMintAuthorityInstruction { + pub accounts: RemoveMintAuthorityInstructionAccountMetas, + pub data: RemoveMintAuthorityInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct RemoveMintAuthorityInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mintAuthority: AccountMeta, + pub admin: AccountMeta, + pub rentDestination: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct RemoveMintAuthorityInstructionAccounts { + pub mintGovernor: Pubkey, + pub mintAuthority: Pubkey, + pub admin: Pubkey, + pub rentDestination: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl RemoveMintAuthorityInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mintAuthority: Pubkey, + admin: Pubkey, + rentDestination: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mintAuthority, + admin, + rentDestination, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, Default)] + pub struct RemoveMintAuthorityInstructionData {} + + impl RemoveMintAuthorityInstructionData { + pub fn new() -> Self { + Self {} + } + } + + impl RemoveMintAuthorityInstruction { + fn discriminator() -> [u8; 8] { + [33, 207, 52, 111, 106, 97, 9, 63] + } + + pub fn data(data: RemoveMintAuthorityInstructionData) -> Self { + Self { + accounts: RemoveMintAuthorityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: RemoveMintAuthorityInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mintAuthority = AccountMeta::new(accounts.mintAuthority, false); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + self.accounts.rentDestination = AccountMeta::new(accounts.rentDestination, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mintAuthority.clone(), + self.accounts.admin.clone(), + self.accounts.rentDestination.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data_no_args(Self::discriminator()), + } + } + } + + pub struct UpdateMintGovernorAdminInstruction { + pub accounts: UpdateMintGovernorAdminInstructionAccountMetas, + pub data: UpdateMintGovernorAdminInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct UpdateMintGovernorAdminInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub admin: AccountMeta, + pub newAdmin: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct UpdateMintGovernorAdminInstructionAccounts { + pub mintGovernor: Pubkey, + pub admin: Pubkey, + pub newAdmin: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl UpdateMintGovernorAdminInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + admin: Pubkey, + newAdmin: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + admin, + newAdmin, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, Default)] + pub struct UpdateMintGovernorAdminInstructionData {} + + impl UpdateMintGovernorAdminInstructionData { + pub fn new() -> Self { + Self {} + } + } + + impl UpdateMintGovernorAdminInstruction { + fn discriminator() -> [u8; 8] { + [13, 24, 126, 16, 56, 4, 123, 179] + } + + pub fn data(data: UpdateMintGovernorAdminInstructionData) -> Self { + Self { + accounts: UpdateMintGovernorAdminInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: UpdateMintGovernorAdminInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + self.accounts.newAdmin = AccountMeta::new_readonly(accounts.newAdmin, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.admin.clone(), + self.accounts.newAdmin.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data_no_args(Self::discriminator()), + } + } + } + + pub struct ReclaimAuthorityInstruction { + pub accounts: ReclaimAuthorityInstructionAccountMetas, + pub data: ReclaimAuthorityInstructionData, + pub remaining_accounts: Vec, + } + + #[derive(Debug, Clone, Default)] + pub struct ReclaimAuthorityInstructionAccountMetas { + pub mintGovernor: AccountMeta, + pub mint: AccountMeta, + pub admin: AccountMeta, + pub newAuthority: AccountMeta, + pub tokenProgram: AccountMeta, + pub eventAuthority: AccountMeta, + pub program: AccountMeta, + } + + #[derive(Debug, Clone)] + pub struct ReclaimAuthorityInstructionAccounts { + pub mintGovernor: Pubkey, + pub mint: Pubkey, + pub admin: Pubkey, + pub newAuthority: Pubkey, + pub tokenProgram: Pubkey, + pub eventAuthority: Pubkey, + pub program: Pubkey, + } + + impl ReclaimAuthorityInstructionAccounts { + pub fn new( + mintGovernor: Pubkey, + mint: Pubkey, + admin: Pubkey, + newAuthority: Pubkey, + tokenProgram: Pubkey, + eventAuthority: Pubkey, + program: Pubkey, + ) -> Self { + Self { + mintGovernor, + mint, + admin, + newAuthority, + tokenProgram, + eventAuthority, + program, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, Default)] + pub struct ReclaimAuthorityInstructionData {} + + impl ReclaimAuthorityInstructionData { + pub fn new() -> Self { + Self {} + } + } + + impl ReclaimAuthorityInstruction { + fn discriminator() -> [u8; 8] { + [156, 147, 67, 113, 251, 229, 162, 166] + } + + pub fn data(data: ReclaimAuthorityInstructionData) -> Self { + Self { + accounts: ReclaimAuthorityInstructionAccountMetas::default(), + data, + remaining_accounts: Vec::new(), + } + } + + pub fn accounts(mut self, accounts: ReclaimAuthorityInstructionAccounts) -> Self { + self.accounts.mintGovernor = AccountMeta::new(accounts.mintGovernor, false); + self.accounts.mint = AccountMeta::new(accounts.mint, false); + self.accounts.admin = AccountMeta::new_readonly(accounts.admin, true); + self.accounts.newAuthority = AccountMeta::new_readonly(accounts.newAuthority, false); + self.accounts.tokenProgram = AccountMeta::new_readonly(accounts.tokenProgram, false); + self.accounts.eventAuthority = + AccountMeta::new_readonly(accounts.eventAuthority, false); + self.accounts.program = AccountMeta::new_readonly(accounts.program, false); + self + } + + fn to_account_metas(&self) -> Vec { + let mut metas = vec![ + self.accounts.mintGovernor.clone(), + self.accounts.mint.clone(), + self.accounts.admin.clone(), + self.accounts.newAuthority.clone(), + self.accounts.tokenProgram.clone(), + self.accounts.eventAuthority.clone(), + self.accounts.program.clone(), + ]; + metas.extend(self.remaining_accounts.clone()); + metas + } + + pub fn instruction(&self) -> Instruction { + Instruction { + program_id: program_id(), + accounts: self.to_account_metas(), + data: ix_data_no_args(Self::discriminator()), + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct MintAuthority { + pub mintGovernor: Pubkey, + pub authorizedMinter: Pubkey, + pub maxTotal: Option, + pub totalMinted: u64, + pub bump: u8, + } + + impl MintAuthority { + pub fn new( + mintGovernor: Pubkey, + authorizedMinter: Pubkey, + maxTotal: Option, + totalMinted: u64, + bump: u8, + ) -> Self { + Self { + mintGovernor, + authorizedMinter, + maxTotal, + totalMinted, + bump, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct MintGovernor { + pub mint: Pubkey, + pub admin: Pubkey, + pub createKey: Pubkey, + pub seqNum: u64, + pub bump: u8, + } + + impl MintGovernor { + pub fn new(mint: Pubkey, admin: Pubkey, createKey: Pubkey, seqNum: u64, bump: u8) -> Self { + Self { + mint, + admin, + createKey, + seqNum, + bump, + } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct AddMintAuthorityArgs { + pub maxTotal: Option, + } + + impl AddMintAuthorityArgs { + pub fn new(maxTotal: Option) -> Self { + Self { maxTotal } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct MintTokensArgs { + pub amount: u64, + } + + impl MintTokensArgs { + pub fn new(amount: u64) -> Self { + Self { amount } + } + } + + #[derive(Debug, BorshDeserialize, BorshSerialize, Clone, PartialEq)] + pub struct UpdateMintAuthorityArgs { + pub maxTotal: Option, + } + + impl UpdateMintAuthorityArgs { + pub fn new(maxTotal: Option) -> Self { + Self { maxTotal } + } + } + + impl AccountDiscriminator for MintAuthority { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + impl AccountDiscriminator for MintGovernor { + fn discriminator() -> &'static [u8] { + &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] + } + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum MintgovernorError { + UnauthorizedAdmin = 6000, + UnauthorizedMinter = 6001, + MintMismatch = 6002, + MintLimitExceeded = 6003, + } + + impl MintgovernorError { + pub fn code(&self) -> u32 { + *self as u32 + } + + pub fn msg(&self) -> &'static str { + match self { + Self::UnauthorizedAdmin => "Unauthorized: signer is not the admin", + Self::UnauthorizedMinter => "Unauthorized: signer is not the authorized minter", + Self::MintMismatch => { + "Mint mismatch: mint_governor.mint does not match provided mint" + } + Self::MintLimitExceeded => "Mint limit exceeded: would exceed max_total", + } + } + + pub fn from_code(code: u32) -> Option { + match code { + 6000 => Some(Self::UnauthorizedAdmin), + 6001 => Some(Self::UnauthorizedMinter), + 6002 => Some(Self::MintMismatch), + 6003 => Some(Self::MintLimitExceeded), + _ => None, + } + } + } +} + +// ============================================================================ +// END OF GENERATED FILE +// ============================================================================ diff --git a/trident-tests/fuzz_0/constants.rs b/trident-tests/fuzz_0/constants.rs new file mode 100644 index 000000000..2fbf77f80 --- /dev/null +++ b/trident-tests/fuzz_0/constants.rs @@ -0,0 +1,20 @@ +// ============================================================================ +// Fuzz Test Configuration +// ============================================================================ +pub const FUZZ_ITERATIONS: u64 = 1000; +pub const FLOWS_PER_ITERATION: u64 = 100; +pub const ROUNDING_TOLERANCE: u64 = 5; + +// ============================================================================ +// Test Token Amounts +// ============================================================================ +pub const TEST_AIRDROP_SOL: u64 = 5_000_000; +pub const TEST_BASE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; +pub const TEST_QUOTE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; // USDC has 6 decimals +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT: u64 = 200_000_000_000; + +// ============================================================================ +// Liquidity Flow Constants +// ============================================================================ +pub const MIN_INITIAL_QUOTE_LIQUIDITY: u64 = 100_000; diff --git a/trident-tests/fuzz_0/flows.rs b/trident-tests/fuzz_0/flows.rs new file mode 100644 index 000000000..a6db69968 --- /dev/null +++ b/trident-tests/fuzz_0/flows.rs @@ -0,0 +1,135 @@ +use crate::constants::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::types::futarchy; +use crate::methods; + +impl FuzzTest { + /// Random provide_liquidity flow - selects random amounts within user's balance + pub fn provide_flow(&mut self, provider: Pubkey, label: &str) { + let provider_quote_balance = self.get_token_balance(self.quote_usdc, provider); + let provider_base_balance = self.get_token_balance(self.base_meta, provider); + if provider_quote_balance == 0 || provider_base_balance == 0 { + return; + } + + let (_, _, total_liquidity) = self.get_spot_reserves(); + + // For empty pool, ensure minimum initial liquidity + let quote_amount = if total_liquidity == 0 { + MIN_INITIAL_QUOTE_LIQUIDITY.min(provider_quote_balance) + } else { + // Random amount up to user's balance + provider_quote_balance.min(self.trident.random_log_uniform()) + }; + + if quote_amount == 0 { + return; + } + + let min_liquidity = if total_liquidity == 0 { 0 } else { 1 }; + self.add_liqidity_checked( + provider, + quote_amount, + provider_base_balance, + min_liquidity, + label, + ); + } + + /// Random withdraw_liquidity flow - selects random amount up to user's position + pub fn withdraw_flow(&mut self, provider: Pubkey, label: &str) { + let provider_liquidity = self.get_position_liquidity(provider); + if provider_liquidity == 0 { + return; + } + + let (_, _, total_liquidity) = self.get_spot_reserves(); + if total_liquidity == 0 { + return; + } + + // Random withdrawal amount up to user's position + let liquidity_to_withdraw: u128 = self.trident.random_log_uniform(); + let liquidity_to_withdraw = liquidity_to_withdraw.min(provider_liquidity); + self.withdraw_liquidity_checked(provider, liquidity_to_withdraw, label); + } + + /// Provides liquidity and checks invariants on success + fn add_liqidity_checked( + &mut self, + provider: Pubkey, + quote_amount: u64, + max_base_amount: u64, + min_liquidity: u128, + label: &str, + ) { + let base_before = self.get_token_balance(self.base_meta, provider); + let quote_before = self.get_token_balance(self.quote_usdc, provider); + let (base_reserve_before, quote_reserve_before, total_liquidity_before) = + self.get_spot_reserves(); + let position_liquidity_before = self.get_position_liquidity(provider); + + let res = methods::futarchy::add_liqidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + provider, + futarchy::ProvideLiquidityParams::new( + quote_amount, + max_base_amount, + min_liquidity, + provider, + ), + Some(label), + ); + + if !res.is_success() { + return; + } + + self.check_provide_invariants( + provider, + base_before, + quote_before, + base_reserve_before, + quote_reserve_before, + total_liquidity_before, + position_liquidity_before, + ); + } + + /// Withdraws liquidity and checks invariants on success + fn withdraw_liquidity_checked( + &mut self, + provider: Pubkey, + liquidity_to_withdraw: u128, + label: &str, + ) { + let base_before = self.get_token_balance(self.base_meta, provider); + let quote_before = self.get_token_balance(self.quote_usdc, provider); + let (base_reserve_before, quote_reserve_before, _) = self.get_spot_reserves(); + + let res = methods::futarchy::withdraw_liquidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + provider, + futarchy::WithdrawLiquidityParams::new(liquidity_to_withdraw, 0, 0), + Some(label), + ); + + if !res.is_success() { + return; + } + + self.check_withdraw_invariants( + provider, + base_before, + quote_before, + base_reserve_before, + quote_reserve_before, + ); + } +} diff --git a/trident-tests/fuzz_0/fuzz_accounts.rs b/trident-tests/fuzz_0/fuzz_accounts.rs new file mode 100644 index 000000000..48272ddb5 --- /dev/null +++ b/trident-tests/fuzz_0/fuzz_accounts.rs @@ -0,0 +1,10 @@ +#![allow(non_snake_case)] + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses {} diff --git a/trident-tests/fuzz_0/helpers.rs b/trident-tests/fuzz_0/helpers.rs new file mode 100644 index 000000000..a145280cb --- /dev/null +++ b/trident-tests/fuzz_0/helpers.rs @@ -0,0 +1,57 @@ +use crate::FuzzTest; +use crate::UserTracking; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::types::futarchy; +use crate::methods; + +impl FuzzTest { + /// Gets the tracking struct for a given provider, creating it if it doesn't exist + pub fn get_user_tracking(&mut self, provider: Pubkey) -> &mut UserTracking { + self.user_tracking.entry(provider).or_default() + } + + /// Gets token balance for a given mint and owner + pub fn get_token_balance(&mut self, mint: Pubkey, owner: Pubkey) -> u64 { + let ata = self + .trident + .get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + match self.trident.get_token_account(ata) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + /// Gets the liquidity amount for a user's AMM position + pub fn get_position_liquidity(&mut self, owner: Pubkey) -> u128 { + let position = methods::pda::get_amm_position_pda(&mut self.trident, self.dao, owner); + match self + .trident + .get_account_with_type::(&position, None) + { + Some(position_account) => position_account.liquidity, + None => 0, + } + } + + /// Gets current spot pool reserves and total liquidity + pub fn get_spot_reserves(&mut self) -> (u64, u64, u128) { + let dao_data = self + .trident + .get_account_with_type::(&self.dao, None) + .expect("Dao not found"); + + match dao_data.amm.state { + futarchy::PoolState::Spot { spot } => ( + spot.baseReserves, + spot.quoteReserves, + dao_data.amm.totalLiquidity, + ), + futarchy::PoolState::Futarchy { .. } => { + panic!("Liquidity fuzz expects DAO AMM to stay in Spot state") + } + } + } +} diff --git a/trident-tests/fuzz_0/invariants.rs b/trident-tests/fuzz_0/invariants.rs new file mode 100644 index 000000000..8a6075600 --- /dev/null +++ b/trident-tests/fuzz_0/invariants.rs @@ -0,0 +1,150 @@ +use crate::constants::ROUNDING_TOLERANCE; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + /// Checks invariants after a successful provide_liquidity operation + #[allow(clippy::too_many_arguments)] + pub fn check_provide_invariants( + &mut self, + provider: Pubkey, + base_before: u64, + quote_before: u64, + base_reserve_before: u64, + quote_reserve_before: u64, + total_liquidity_before: u128, + position_liquidity_before: u128, + ) { + let base_after = self.get_token_balance(self.base_meta, provider); + let quote_after = self.get_token_balance(self.quote_usdc, provider); + let (base_reserve_after, quote_reserve_after, total_liquidity_after) = + self.get_spot_reserves(); + let position_liquidity_after = self.get_position_liquidity(provider); + + // Calculate and track actual amounts deposited + let base_deposited = base_before.saturating_sub(base_after); + let quote_deposited = quote_before.saturating_sub(quote_after); + + let tracking = self.get_user_tracking(provider); + tracking.base_deposited = tracking.base_deposited.saturating_add(base_deposited); + tracking.quote_deposited = tracking.quote_deposited.saturating_add(quote_deposited); + + // Simple invariants: balances decrease, reserves increase, liquidity increases + assert!(base_after <= base_before); + assert!(quote_after <= quote_before); + assert!(base_reserve_after >= base_reserve_before); + assert!(quote_reserve_after >= quote_reserve_before); + assert!(total_liquidity_after >= total_liquidity_before); + assert!(position_liquidity_after >= position_liquidity_before); + } + + /// Checks invariants after a successful withdraw_liquidity operation + pub fn check_withdraw_invariants( + &mut self, + provider: Pubkey, + base_before: u64, + quote_before: u64, + base_reserve_before: u64, + quote_reserve_before: u64, + ) { + let base_after = self.get_token_balance(self.base_meta, provider); + let quote_after = self.get_token_balance(self.quote_usdc, provider); + let (base_reserve_after, quote_reserve_after, _) = self.get_spot_reserves(); + + // Calculate and track actual amounts withdrawn + let base_withdrawn = base_after.saturating_sub(base_before); + let quote_withdrawn = quote_after.saturating_sub(quote_before); + + let tracking = self.get_user_tracking(provider); + tracking.base_withdrawn = tracking.base_withdrawn.saturating_add(base_withdrawn); + tracking.quote_withdrawn = tracking.quote_withdrawn.saturating_add(quote_withdrawn); + + // Per-user check: withdrawn should not significantly exceed deposited. + // A tolerance of 1 token is allowed because floor division in partial + // withdrawals leaves rounding dust in the pool, which gets redistributed + // to remaining LP holders. This is standard AMM behavior, not a bug. + // Any difference > 1 would indicate a real accounting issue. + let base_withdrawn_total = tracking.base_withdrawn; + let base_deposited_total = tracking.base_deposited; + let quote_withdrawn_total = tracking.quote_withdrawn; + let quote_deposited_total = tracking.quote_deposited; + + if base_withdrawn_total > base_deposited_total.saturating_add(ROUNDING_TOLERANCE) { + let difference = base_withdrawn_total - base_deposited_total; + panic!( + "User {:?} has withdrawn more base tokens ({}) than deposited ({}). Difference: {}. This exceeds rounding tolerance and indicates a theft bug!", + provider, + base_withdrawn_total, + base_deposited_total, + difference + ); + } + if quote_withdrawn_total > quote_deposited_total.saturating_add(ROUNDING_TOLERANCE) { + let difference = quote_withdrawn_total - quote_deposited_total; + panic!( + "User {:?} has withdrawn more quote tokens ({}) than deposited ({}). Difference: {}. This exceeds rounding tolerance and indicates a theft bug!", + provider, + quote_withdrawn_total, + quote_deposited_total, + difference + ); + } + + // Simple invariants: balances increase, reserves decrease + assert!(base_after >= base_before); + assert!(quote_after >= quote_before); + assert!(base_reserve_after <= base_reserve_before); + assert!(quote_reserve_after <= quote_reserve_before); + } + + /// Global invariants checked at the end of each iteration + pub fn assert_global_invariants(&mut self) { + let (base_reserves, quote_reserves, total_liquidity) = self.get_spot_reserves(); + let dao_base_balance = self.get_token_balance(self.base_meta, self.dao); + let dao_quote_balance = self.get_token_balance(self.quote_usdc, self.dao); + + // Reserve accounting should match DAO vault balances + assert_eq!(base_reserves, dao_base_balance); + assert_eq!(quote_reserves, dao_quote_balance); + + // Empty pool consistency check + if total_liquidity == 0 { + assert_eq!( + base_reserves, 0, + "If total liquidity is 0, base reserves should be 0" + ); + assert_eq!( + quote_reserves, 0, + "If total liquidity is 0, quote reserves should be 0" + ); + } + + for (user_pubkey, tracking) in &self.user_tracking { + let base_withdrawn = tracking.base_withdrawn; + let base_deposited = tracking.base_deposited; + let quote_withdrawn = tracking.quote_withdrawn; + let quote_deposited = tracking.quote_deposited; + + if base_withdrawn > base_deposited.saturating_add(ROUNDING_TOLERANCE) { + let difference = base_withdrawn - base_deposited; + panic!( + "User {:?} has withdrawn more base tokens ({}) than deposited ({}). Difference: {}. This exceeds rounding tolerance and indicates a theft bug!", + user_pubkey, + base_withdrawn, + base_deposited, + difference + ); + } + if quote_withdrawn > quote_deposited.saturating_add(ROUNDING_TOLERANCE) { + let difference = quote_withdrawn - quote_deposited; + panic!( + "User {:?} has withdrawn more quote tokens ({}) than deposited ({}). Difference: {}. This exceeds rounding tolerance and indicates a theft bug!", + user_pubkey, + quote_withdrawn, + quote_deposited, + difference + ); + } + } + } +} diff --git a/trident-tests/fuzz_0/methods.rs b/trident-tests/fuzz_0/methods.rs new file mode 100644 index 000000000..a17da5df8 --- /dev/null +++ b/trident-tests/fuzz_0/methods.rs @@ -0,0 +1,142 @@ +use crate::constants::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +// Re-export common modules for convenience +pub use crate::common::futarchy; +pub use crate::common::pda; +pub use crate::common::squads; +pub use crate::common::token; + +impl FuzzTest { + pub fn airdrop_accounts(&mut self) { + self.trident + .airdrop(&self.alice.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.bob.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.payer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident.airdrop( + &self.dao_creator.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.base_meta_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.quote_usdc_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.initial_liquidity_provider.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + } + + pub fn initial_setup(&mut self) { + token::initialize_mint( + &mut self.trident, + self.base_meta_mint_owner.pubkey(), + self.base_meta, + 6, + self.base_meta_mint_owner.pubkey(), + None, + None, + ); + + token::initialize_mint( + &mut self.trident, + self.quote_usdc_mint_owner.pubkey(), + self.quote_usdc, + 6, + self.quote_usdc_mint_owner.pubkey(), + None, + None, + ); + + let alice_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.alice.pubkey(), + ); + let alice_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.alice.pubkey(), + ); + + let bob_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.bob.pubkey(), + ); + let bob_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.bob.pubkey(), + ); + + let initial_liquidity_provider_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.initial_liquidity_provider.pubkey(), + ); + let initial_liquidity_provider_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.initial_liquidity_provider.pubkey(), + ); + + token::mint_to( + &mut self.trident, + alice_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + alice_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + bob_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + bob_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + initial_liquidity_provider_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + initial_liquidity_provider_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + ); + } +} diff --git a/trident-tests/fuzz_0/test_fuzz.rs b/trident-tests/fuzz_0/test_fuzz.rs new file mode 100644 index 000000000..cc96b2c9b --- /dev/null +++ b/trident-tests/fuzz_0/test_fuzz.rs @@ -0,0 +1,169 @@ +use fuzz_accounts::*; +use std::collections::HashMap; +use trident_fuzz::fuzzing::*; + +#[path = "../common/mod.rs"] +pub mod common; +pub mod methods; + +mod constants; +mod fuzz_accounts; + +mod flows; +mod helpers; +mod invariants; + +use crate::constants::*; +use common::types::futarchy; +use common::types::futarchy::InitializeDaoParams; + +// Tracks cumulative deposits and withdrawals per user to detect theft bugs +#[derive(Default)] +pub struct UserTracking { + pub base_deposited: u64, + pub quote_deposited: u64, + pub base_withdrawn: u64, + pub quote_withdrawn: u64, +} + +/// Main fuzz test structure for liquidity operations +#[derive(FuzzTestMethods)] +struct FuzzTest { + trident: Trident, + fuzz_accounts: AccountAddresses, + + alice: Keypair, + bob: Keypair, + initial_liquidity_provider: Keypair, + payer: Keypair, + dao_creator: Keypair, + base_meta_mint_owner: Keypair, + quote_usdc_mint_owner: Keypair, + + base_meta: Pubkey, + quote_usdc: Pubkey, + dao: Pubkey, + + // Track deposits and withdrawals per user (keyed by Pubkey) + user_tracking: HashMap, +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + let alice = trident.random_keypair(); + let bob = trident.random_keypair(); + let initial_liquidity_provider = trident.random_keypair(); + let payer = trident.random_keypair(); + let dao_creator = trident.random_keypair(); + let base_meta_mint_owner = trident.random_keypair(); + let quote_usdc_mint_owner = trident.random_keypair(); + let base_meta = trident.random_keypair().pubkey(); + let quote_usdc = trident.random_keypair().pubkey(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + alice, + bob, + initial_liquidity_provider, + payer, + dao_creator, + base_meta_mint_owner, + quote_usdc_mint_owner, + base_meta, + quote_usdc, + dao: Pubkey::default(), + user_tracking: HashMap::new(), + } + } + + /// Initializes the test environment: airdrops, mints tokens, creates DAO, and seeds initial liquidity + #[init] + fn start(&mut self) { + self.airdrop_accounts(); + self.initial_setup(); + + // Initialize DAO with minimal parameters for liquidity testing + let init_dao_params = InitializeDaoParams::new( + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 86401, + 0, + None, + 0, + self.dao_creator.pubkey(), + ); + + let (dao, _multisig) = methods::futarchy::initialize_dao( + &mut self.trident, + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.base_meta, + self.quote_usdc, + init_dao_params, + Some("Initialize DAO for Liquidity Fuzz"), + ); + + self.dao = dao; + + // Seed the pool with one deterministic deposit so randomized withdraw + // flows have a valid state to operate on. + let res = methods::futarchy::add_liqidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.initial_liquidity_provider.pubkey(), + futarchy::ProvideLiquidityParams::new( + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + 0, + self.initial_liquidity_provider.pubkey(), + ), + Some("Initial Liquidity"), + ); + + assert!(res.is_success(), "Initial liquidity seed must succeed"); + } + + /// Flow: Alice provides liquidity with random amounts + #[flow] + fn flow_provide_liquidity_alice(&mut self) { + self.provide_flow(self.alice.pubkey(), "Provide Liquidity - Alice"); + } + + /// Flow: Bob provides liquidity with random amounts + #[flow] + fn flow_provide_liquidity_bob(&mut self) { + self.provide_flow(self.bob.pubkey(), "Provide Liquidity - Bob"); + } + + /// Flow: Alice withdraws liquidity with random amounts + #[flow] + fn flow_withdraw_liquidity_alice(&mut self) { + self.withdraw_flow(self.alice.pubkey(), "Withdraw Liquidity - Alice"); + } + + /// Flow: Bob withdraws liquidity with random amounts + #[flow] + fn flow_withdraw_liquidity_bob(&mut self) { + self.withdraw_flow(self.bob.pubkey(), "Withdraw Liquidity - Bob"); + } + + /// Final check: validates all global invariants at the end of each iteration + #[end] + fn end(&mut self) { + self.assert_global_invariants(); + } +} + +fn main() { + // fuzz(iterations, flows_per_iteration) + FuzzTest::fuzz(FUZZ_ITERATIONS, FLOWS_PER_ITERATION); +} diff --git a/trident-tests/fuzz_1/constants.rs b/trident-tests/fuzz_1/constants.rs new file mode 100644 index 000000000..016c0d793 --- /dev/null +++ b/trident-tests/fuzz_1/constants.rs @@ -0,0 +1,14 @@ +// ============================================================================ +// Fuzz Test Configuration +// ============================================================================ +pub const FUZZ_ITERATIONS: u64 = 10000; +pub const FLOWS_PER_ITERATION: u64 = 50; + +// ============================================================================ +// Test Token Amounts +// ============================================================================ +pub const TEST_AIRDROP_SOL: u64 = 5_000_000; +pub const TEST_BASE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; +pub const TEST_QUOTE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; // USDC has 6 decimals +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT: u64 = 200_000_000_000; diff --git a/trident-tests/fuzz_1/flows.rs b/trident-tests/fuzz_1/flows.rs new file mode 100644 index 000000000..cc742c4f9 --- /dev/null +++ b/trident-tests/fuzz_1/flows.rs @@ -0,0 +1,87 @@ +use crate::common::types::futarchy; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + /// Random spot swap flow - selects random swap type and amount + pub fn swap_flow(&mut self, trader: Pubkey, label: &str) { + let trader_base_balance = self.get_token_balance(self.base_meta, trader); + let trader_quote_balance = self.get_token_balance(self.quote_usdc, trader); + let (base_reserves, quote_reserves, _, _, _) = self.get_spot_reserves(); + + // Need liquidity in pool to trade + if base_reserves == 0 || quote_reserves == 0 { + return; + } + + // Randomly choose buy or sell + let swap_type = if self.trident.random_bool() { + futarchy::SwapType::Buy + } else { + futarchy::SwapType::Sell + }; + + let (input_amount, has_balance) = match swap_type { + futarchy::SwapType::Buy => { + // Buying base with quote - need quote balance + let max_input = trader_quote_balance.min(quote_reserves); + if max_input == 0 { + return; + } + let input: u64 = self.trident.random_log_uniform(); + let input = input.min(max_input); + (input, input > 0) + } + futarchy::SwapType::Sell => { + // Selling base for quote - need base balance + let max_input = trader_base_balance.min(base_reserves); + if max_input == 0 { + return; + } + let input: u64 = self.trident.random_log_uniform(); + let input = input.min(max_input); + (input, input > 0) + } + }; + + if !has_balance { + return; + } + + self.spot_swap_checked(trader, swap_type, input_amount, label); + } + + /// Executes spot swap and checks invariants on success + fn spot_swap_checked( + &mut self, + trader: Pubkey, + swap_type: futarchy::SwapType, + input_amount: u64, + label: &str, + ) { + let base_before = self.get_token_balance(self.base_meta, trader); + let quote_before = self.get_token_balance(self.quote_usdc, trader); + let (base_reserve_before, quote_reserve_before, _, _, _) = self.get_spot_reserves(); + + let res = crate::methods::futarchy::spot_swap( + &mut self.trident, + self.dao, + trader, + futarchy::SpotSwapParams::new(input_amount, swap_type.clone(), 0), + Some(label), + ); + + if !res.is_success() { + return; + } + + self.check_swap_invariants( + trader, + swap_type, + base_before, + quote_before, + base_reserve_before, + quote_reserve_before, + ); + } +} diff --git a/trident-tests/fuzz_1/fuzz_accounts.rs b/trident-tests/fuzz_1/fuzz_accounts.rs new file mode 100644 index 000000000..48272ddb5 --- /dev/null +++ b/trident-tests/fuzz_1/fuzz_accounts.rs @@ -0,0 +1,10 @@ +#![allow(non_snake_case)] + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses {} diff --git a/trident-tests/fuzz_1/helpers.rs b/trident-tests/fuzz_1/helpers.rs new file mode 100644 index 000000000..3447c5620 --- /dev/null +++ b/trident-tests/fuzz_1/helpers.rs @@ -0,0 +1,40 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::types::futarchy; + +impl FuzzTest { + /// Gets token balance for a given mint and owner + pub fn get_token_balance(&mut self, mint: Pubkey, owner: Pubkey) -> u64 { + let ata = self + .trident + .get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + match self.trident.get_token_account(ata) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + /// Gets current spot pool reserves, total liquidity, and protocol fee balances + pub fn get_spot_reserves(&mut self) -> (u64, u64, u128, u64, u64) { + let dao_data = self + .trident + .get_account_with_type::(&self.dao, None) + .expect("Dao not found"); + + match dao_data.amm.state { + futarchy::PoolState::Spot { spot } => ( + spot.baseReserves, + spot.quoteReserves, + dao_data.amm.totalLiquidity, + spot.baseProtocolFeeBalance, + spot.quoteProtocolFeeBalance, + ), + futarchy::PoolState::Futarchy { .. } => { + panic!("Spot trading fuzz expects DAO AMM to stay in Spot state") + } + } + } +} diff --git a/trident-tests/fuzz_1/invariants.rs b/trident-tests/fuzz_1/invariants.rs new file mode 100644 index 000000000..c7e2b2f6c --- /dev/null +++ b/trident-tests/fuzz_1/invariants.rs @@ -0,0 +1,101 @@ +use crate::common::types::futarchy; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + /// Checks invariants after a successful spot_swap operation + pub fn check_swap_invariants( + &mut self, + trader: Pubkey, + swap_type: futarchy::SwapType, + base_before: u64, + quote_before: u64, + base_reserve_before: u64, + quote_reserve_before: u64, + ) { + let base_after = self.get_token_balance(self.base_meta, trader); + let quote_after = self.get_token_balance(self.quote_usdc, trader); + let (base_reserve_after, quote_reserve_after, _, _, _) = self.get_spot_reserves(); + + match swap_type { + futarchy::SwapType::Buy => { + // Buying base with quote: base increases, quote decreases + // Pool: base decreases, quote increases + assert!( + base_after >= base_before, + "Buy swap: trader base balance should increase or stay same" + ); + assert!( + quote_after <= quote_before, + "Buy swap: trader quote balance should decrease" + ); + assert!( + base_reserve_after <= base_reserve_before, + "Buy swap: pool base reserves should decrease" + ); + assert!( + quote_reserve_after >= quote_reserve_before, + "Buy swap: pool quote reserves should increase" + ); + } + futarchy::SwapType::Sell => { + // Selling base for quote: base decreases, quote increases + // Pool: base increases, quote decreases + assert!( + base_after <= base_before, + "Sell swap: trader base balance should decrease" + ); + assert!( + quote_after >= quote_before, + "Sell swap: trader quote balance should increase or stay same" + ); + assert!( + base_reserve_after >= base_reserve_before, + "Sell swap: pool base reserves should increase" + ); + assert!( + quote_reserve_after <= quote_reserve_before, + "Sell swap: pool quote reserves should decrease" + ); + } + } + } + + /// Global invariants checked at the end of each iteration + pub fn assert_global_invariants(&mut self) { + let (base_reserves, quote_reserves, total_liquidity, base_fees, quote_fees) = + self.get_spot_reserves(); + let dao_base_balance = self.get_token_balance(self.base_meta, self.dao); + let dao_quote_balance = self.get_token_balance(self.quote_usdc, self.dao); + + // DAO vault balance = reserves + accumulated protocol fees + assert_eq!( + base_reserves + base_fees, + dao_base_balance, + "Base reserves + fees should match DAO vault balance. Reserves: {}, Fees: {}, Vault: {}", + base_reserves, + base_fees, + dao_base_balance + ); + assert_eq!( + quote_reserves + quote_fees, + dao_quote_balance, + "Quote reserves + fees should match DAO vault balance. Reserves: {}, Fees: {}, Vault: {}", + quote_reserves, + quote_fees, + dao_quote_balance + ); + + // Empty pool consistency check + if total_liquidity == 0 { + assert_eq!( + base_reserves, 0, + "If total liquidity is 0, base reserves should be 0" + ); + assert_eq!( + quote_reserves, 0, + "If total liquidity is 0, quote reserves should be 0" + ); + } + } +} diff --git a/trident-tests/fuzz_1/methods.rs b/trident-tests/fuzz_1/methods.rs new file mode 100644 index 000000000..a17da5df8 --- /dev/null +++ b/trident-tests/fuzz_1/methods.rs @@ -0,0 +1,142 @@ +use crate::constants::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +// Re-export common modules for convenience +pub use crate::common::futarchy; +pub use crate::common::pda; +pub use crate::common::squads; +pub use crate::common::token; + +impl FuzzTest { + pub fn airdrop_accounts(&mut self) { + self.trident + .airdrop(&self.alice.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.bob.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.payer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident.airdrop( + &self.dao_creator.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.base_meta_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.quote_usdc_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.initial_liquidity_provider.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + } + + pub fn initial_setup(&mut self) { + token::initialize_mint( + &mut self.trident, + self.base_meta_mint_owner.pubkey(), + self.base_meta, + 6, + self.base_meta_mint_owner.pubkey(), + None, + None, + ); + + token::initialize_mint( + &mut self.trident, + self.quote_usdc_mint_owner.pubkey(), + self.quote_usdc, + 6, + self.quote_usdc_mint_owner.pubkey(), + None, + None, + ); + + let alice_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.alice.pubkey(), + ); + let alice_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.alice.pubkey(), + ); + + let bob_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.bob.pubkey(), + ); + let bob_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.bob.pubkey(), + ); + + let initial_liquidity_provider_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.initial_liquidity_provider.pubkey(), + ); + let initial_liquidity_provider_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.initial_liquidity_provider.pubkey(), + ); + + token::mint_to( + &mut self.trident, + alice_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + alice_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + bob_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + bob_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + initial_liquidity_provider_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + initial_liquidity_provider_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + ); + } +} diff --git a/trident-tests/fuzz_1/test_fuzz.rs b/trident-tests/fuzz_1/test_fuzz.rs new file mode 100644 index 000000000..c32204917 --- /dev/null +++ b/trident-tests/fuzz_1/test_fuzz.rs @@ -0,0 +1,143 @@ +use fuzz_accounts::*; +use trident_fuzz::fuzzing::*; + +mod constants; +mod fuzz_accounts; + +pub mod methods; + +#[path = "../common/mod.rs"] +pub mod common; + +mod flows; +mod helpers; +mod invariants; + +use common::types::futarchy; +use common::types::futarchy::InitializeDaoParams; +use constants::*; + +/// Main fuzz test structure for spot trading operations +#[derive(FuzzTestMethods)] +struct FuzzTest { + trident: Trident, + fuzz_accounts: AccountAddresses, + + alice: Keypair, + bob: Keypair, + initial_liquidity_provider: Keypair, + payer: Keypair, + dao_creator: Keypair, + base_meta_mint_owner: Keypair, + quote_usdc_mint_owner: Keypair, + + base_meta: Pubkey, + quote_usdc: Pubkey, + dao: Pubkey, +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + let alice = trident.random_keypair(); + let bob = trident.random_keypair(); + let initial_liquidity_provider = trident.random_keypair(); + let payer = trident.random_keypair(); + let dao_creator = trident.random_keypair(); + let base_meta_mint_owner = trident.random_keypair(); + let quote_usdc_mint_owner = trident.random_keypair(); + let base_meta = trident.random_keypair().pubkey(); + let quote_usdc = trident.random_keypair().pubkey(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + alice, + bob, + initial_liquidity_provider, + payer, + dao_creator, + base_meta_mint_owner, + quote_usdc_mint_owner, + base_meta, + quote_usdc, + dao: Pubkey::default(), + } + } + + /// Initializes the test environment: airdrops, mints tokens, creates DAO, and seeds initial liquidity + #[init] + fn start(&mut self) { + self.airdrop_accounts(); + self.initial_setup(); + + // Initialize DAO with minimal parameters for spot trading + let init_dao_params = InitializeDaoParams::new( + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 86401, + 0, + None, + 0, + self.dao_creator.pubkey(), + ); + + let (dao, _multisig) = methods::futarchy::initialize_dao( + &mut self.trident, + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.base_meta, + self.quote_usdc, + init_dao_params, + Some("Initialize DAO for Spot Trading Fuzz"), + ); + + self.dao = dao; + + // Seed the pool with initial liquidity so trading can occur + let res = methods::futarchy::add_liqidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.initial_liquidity_provider.pubkey(), + futarchy::ProvideLiquidityParams::new( + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + 0, + self.initial_liquidity_provider.pubkey(), + ), + Some("Initial Liquidity"), + ); + + assert!(res.is_success(), "Initial liquidity seed must succeed"); + } + + /// Flow: Alice performs a spot swap (buy or sell) + #[flow] + fn flow_spot_swap_alice(&mut self) { + self.swap_flow(self.alice.pubkey(), "Spot Swap - Alice"); + } + + /// Flow: Bob performs a spot swap (buy or sell) + #[flow] + fn flow_spot_swap_bob(&mut self) { + self.swap_flow(self.bob.pubkey(), "Spot Swap - Bob"); + } + + /// Final check: validates all global invariants at the end of each iteration + #[end] + fn end(&mut self) { + self.assert_global_invariants(); + } +} + +fn main() { + // fuzz(iterations, flows_per_iteration) + FuzzTest::fuzz(FUZZ_ITERATIONS, FLOWS_PER_ITERATION); +} diff --git a/trident-tests/fuzz_2/constants.rs b/trident-tests/fuzz_2/constants.rs new file mode 100644 index 000000000..03ae7370b --- /dev/null +++ b/trident-tests/fuzz_2/constants.rs @@ -0,0 +1,15 @@ +// ============================================================================ +// Fuzz Test Configuration +// ============================================================================ +pub const FUZZ_ITERATIONS: u64 = 1000; +pub const FLOWS_PER_ITERATION: u64 = 50; + +// ============================================================================ +// Test Token Amounts +// ============================================================================ +pub const TEST_AIRDROP_SOL: u64 = 5_000_000; +pub const TEST_BASE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; +pub const TEST_QUOTE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; // USDC has 6 decimals +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_STAKE_AMOUNT: u64 = 1_000_000; diff --git a/trident-tests/fuzz_2/flows.rs b/trident-tests/fuzz_2/flows.rs new file mode 100644 index 000000000..ed45c95db --- /dev/null +++ b/trident-tests/fuzz_2/flows.rs @@ -0,0 +1,136 @@ +use crate::common::types::futarchy; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + /// Random conditional swap flow (pass/fail and buy/sell). + pub fn conditional_swap_flow(&mut self, trader: Pubkey, label: &str) { + let market = if self.trident.random_bool() { + futarchy::Market::Pass + } else { + futarchy::Market::Fail + }; + + let swap_type = if self.trident.random_bool() { + futarchy::SwapType::Buy + } else { + futarchy::SwapType::Sell + }; + + if !self.ensure_conditional_input_balance(trader, &market, &swap_type) { + return; + } + + let (input_mint, output_mint) = self.get_market_input_output_mints(&market, &swap_type); + let input_balance = self.get_token_balance(input_mint, trader); + + if input_balance == 0 { + return; + } + + let mut input_amount: u64 = self.trident.random_log_uniform(); + input_amount = input_amount.min(input_balance); + if input_amount == 0 { + return; + } + + self.conditional_swap_checked( + trader, + market, + swap_type, + input_mint, + output_mint, + input_amount, + label, + ); + } + + fn ensure_conditional_input_balance( + &mut self, + trader: Pubkey, + market: &futarchy::Market, + swap_type: &futarchy::SwapType, + ) -> bool { + let (input_mint, _) = self.get_market_input_output_mints(market, swap_type); + if self.get_token_balance(input_mint, trader) > 0 { + return true; + } + + let (vault_to_split, underlying_mint) = match swap_type { + futarchy::SwapType::Buy => (self.quote_vault, self.quote_usdc), + futarchy::SwapType::Sell => (self.base_vault, self.base_meta), + }; + + let underlying_balance = self.get_token_balance(underlying_mint, trader); + if underlying_balance == 0 { + return false; + } + + let mut split_amount: u64 = self.trident.random_log_uniform(); + split_amount = split_amount.min(underlying_balance); + if split_amount == 0 { + return false; + } + + crate::methods::conditional_vault::split_tokens( + &mut self.trident, + self.payer.pubkey(), + self.question, + vault_to_split, + split_amount, + trader, + Some("Prepare conditional input balance"), + ); + + self.get_token_balance(input_mint, trader) > 0 + } + + #[allow(clippy::too_many_arguments)] + fn conditional_swap_checked( + &mut self, + trader: Pubkey, + market: futarchy::Market, + swap_type: futarchy::SwapType, + input_mint: Pubkey, + output_mint: Pubkey, + input_amount: u64, + label: &str, + ) { + let input_before = self.get_token_balance(input_mint, trader); + let output_before = self.get_token_balance(output_mint, trader); + let market_pool_before = self.get_market_pool(&market); + + let res = crate::methods::futarchy::conditional_swap( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.proposal, + trader, + self.question, + self.base_vault, + self.quote_vault, + futarchy::ConditionalSwapParams::new( + market.clone(), + swap_type.clone(), + input_amount, + 0, + ), + Some(label), + ); + + if !res.is_success() { + return; + } + + self.check_conditional_swap_invariants( + trader, + market, + swap_type, + input_mint, + output_mint, + input_before, + output_before, + market_pool_before, + ); + } +} diff --git a/trident-tests/fuzz_2/fuzz_accounts.rs b/trident-tests/fuzz_2/fuzz_accounts.rs new file mode 100644 index 000000000..48272ddb5 --- /dev/null +++ b/trident-tests/fuzz_2/fuzz_accounts.rs @@ -0,0 +1,10 @@ +#![allow(non_snake_case)] + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses {} diff --git a/trident-tests/fuzz_2/helpers.rs b/trident-tests/fuzz_2/helpers.rs new file mode 100644 index 000000000..ace680949 --- /dev/null +++ b/trident-tests/fuzz_2/helpers.rs @@ -0,0 +1,105 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::types::futarchy; + +impl FuzzTest { + /// Gets token balance for a given mint and owner. + pub fn get_token_balance(&mut self, mint: Pubkey, owner: Pubkey) -> u64 { + let ata = self + .trident + .get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + match self.trident.get_token_account(ata) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + /// Returns spot/pass/fail pools from the DAO in Futarchy mode. + pub fn get_futarchy_pools(&mut self) -> (futarchy::Pool, futarchy::Pool, futarchy::Pool) { + let dao_data = self + .trident + .get_account_with_type::(&self.dao, None) + .expect("Dao not found"); + + match dao_data.amm.state { + futarchy::PoolState::Futarchy { spot, pass, fail } => (spot, pass, fail), + futarchy::PoolState::Spot { .. } => { + panic!("Conditional trading fuzz expects DAO AMM to be in Futarchy state") + } + } + } + + /// Gets proposal conditional token mints (pass base/quote, fail base/quote). + pub fn get_conditional_mints(&mut self) -> (Pubkey, Pubkey, Pubkey, Pubkey) { + let proposal_data = self + .trident + .get_account_with_type::(&self.proposal, None) + .expect("Proposal not found"); + + ( + proposal_data.passBaseMint, + proposal_data.passQuoteMint, + proposal_data.failBaseMint, + proposal_data.failQuoteMint, + ) + } + + pub fn get_market_pool(&mut self, market: &futarchy::Market) -> futarchy::Pool { + let (_, pass, fail) = self.get_futarchy_pools(); + match market { + futarchy::Market::Pass => pass, + futarchy::Market::Fail => fail, + futarchy::Market::Spot => panic!("Conditional trading fuzz only supports pass/fail"), + } + } + + pub fn get_market_input_output_mints( + &mut self, + market: &futarchy::Market, + swap_type: &futarchy::SwapType, + ) -> (Pubkey, Pubkey) { + let (pass_base_mint, pass_quote_mint, fail_base_mint, fail_quote_mint) = + self.get_conditional_mints(); + + match (market, swap_type) { + (futarchy::Market::Pass, futarchy::SwapType::Buy) => (pass_quote_mint, pass_base_mint), + (futarchy::Market::Pass, futarchy::SwapType::Sell) => (pass_base_mint, pass_quote_mint), + (futarchy::Market::Fail, futarchy::SwapType::Buy) => (fail_quote_mint, fail_base_mint), + (futarchy::Market::Fail, futarchy::SwapType::Sell) => (fail_base_mint, fail_quote_mint), + (futarchy::Market::Spot, _) => { + panic!("Conditional trading fuzz only supports pass/fail") + } + } + } + + pub fn get_token_account_balance(&mut self, token_account: Pubkey) -> u64 { + match self.trident.get_token_account(token_account) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + pub fn get_underlying_vault_accounts(&mut self) -> (Pubkey, Pubkey) { + let base_vault_data = self + .trident + .get_account_with_type::( + &self.base_vault, + None, + ) + .expect("Base conditional vault not found"); + let quote_vault_data = self + .trident + .get_account_with_type::( + &self.quote_vault, + None, + ) + .expect("Quote conditional vault not found"); + ( + base_vault_data.underlyingTokenAccount, + quote_vault_data.underlyingTokenAccount, + ) + } +} diff --git a/trident-tests/fuzz_2/invariants.rs b/trident-tests/fuzz_2/invariants.rs new file mode 100644 index 000000000..bfc47f4c2 --- /dev/null +++ b/trident-tests/fuzz_2/invariants.rs @@ -0,0 +1,156 @@ +use crate::common::types::futarchy; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + pub fn capture_balance_tracking_snapshot(&mut self) { + self.tracking.base_underlying_total = self.current_total_base_underlying(); + self.tracking.quote_underlying_total = self.current_total_quote_underlying(); + } + + fn current_total_base_underlying(&mut self) -> u128 { + let (base_underlying_vault_ata, _) = self.get_underlying_vault_accounts(); + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + owners + .iter() + .map(|owner| self.get_token_balance(self.base_meta, *owner) as u128) + .sum::() + + self.get_token_account_balance(base_underlying_vault_ata) as u128 + } + + fn current_total_quote_underlying(&mut self) -> u128 { + let (_, quote_underlying_vault_ata) = self.get_underlying_vault_accounts(); + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + owners + .iter() + .map(|owner| self.get_token_balance(self.quote_usdc, *owner) as u128) + .sum::() + + self.get_token_account_balance(quote_underlying_vault_ata) as u128 + } + + fn current_total_conditional_pair_for_owner_set( + &mut self, + pass_mint: Pubkey, + fail_mint: Pubkey, + ) -> (u128, u128) { + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + let pass_total = owners + .iter() + .map(|owner| self.get_token_balance(pass_mint, *owner) as u128) + .sum(); + let fail_total = owners + .iter() + .map(|owner| self.get_token_balance(fail_mint, *owner) as u128) + .sum(); + (pass_total, fail_total) + } + + #[allow(clippy::too_many_arguments)] + pub fn check_conditional_swap_invariants( + &mut self, + trader: Pubkey, + market: futarchy::Market, + swap_type: futarchy::SwapType, + input_mint: Pubkey, + output_mint: Pubkey, + input_before: u64, + output_before: u64, + market_pool_before: futarchy::Pool, + ) { + let input_after = self.get_token_balance(input_mint, trader); + let output_after = self.get_token_balance(output_mint, trader); + let market_pool_after = self.get_market_pool(&market); + + assert!( + input_after <= input_before, + "Trader input token balance must not increase after swap" + ); + assert!( + output_after >= output_before, + "Trader output token balance must not decrease after swap" + ); + + match swap_type { + futarchy::SwapType::Buy => { + assert!( + market_pool_after.quoteReserves >= market_pool_before.quoteReserves, + "Buy in conditional market should increase quote reserves" + ); + assert!( + market_pool_after.baseReserves <= market_pool_before.baseReserves, + "Buy in conditional market should decrease base reserves" + ); + } + futarchy::SwapType::Sell => { + assert!( + market_pool_after.baseReserves >= market_pool_before.baseReserves, + "Sell in conditional market should increase base reserves" + ); + assert!( + market_pool_after.quoteReserves <= market_pool_before.quoteReserves, + "Sell in conditional market should decrease quote reserves" + ); + } + } + } + + pub fn assert_global_invariants(&mut self) { + let (pass_base_mint, pass_quote_mint, fail_base_mint, fail_quote_mint) = + self.get_conditional_mints(); + + assert_eq!( + self.current_total_base_underlying(), + self.tracking.base_underlying_total, + "Base underlying total should be conserved across known holders" + ); + assert_eq!( + self.current_total_quote_underlying(), + self.tracking.quote_underlying_total, + "Quote underlying total should be conserved across known holders" + ); + + let (pass_base_total, fail_base_total) = + self.current_total_conditional_pair_for_owner_set(pass_base_mint, fail_base_mint); + assert_eq!( + pass_base_total, fail_base_total, + "Pass/fail base conditional totals should remain equal over tracked owners" + ); + + let (pass_quote_total, fail_quote_total) = + self.current_total_conditional_pair_for_owner_set(pass_quote_mint, fail_quote_mint); + assert_eq!( + pass_quote_total, fail_quote_total, + "Pass/fail quote conditional totals should remain equal over tracked owners" + ); + } +} diff --git a/trident-tests/fuzz_2/methods.rs b/trident-tests/fuzz_2/methods.rs new file mode 100644 index 000000000..56e62d2dc --- /dev/null +++ b/trident-tests/fuzz_2/methods.rs @@ -0,0 +1,162 @@ +use crate::constants::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +// Re-export common modules for convenience +pub use crate::common::conditional_vault; +pub use crate::common::futarchy; +pub use crate::common::pda; +pub use crate::common::squads; +pub use crate::common::token; + +impl FuzzTest { + pub fn airdrop_accounts(&mut self) { + self.trident + .airdrop(&self.alice.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.bob.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.payer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident.airdrop( + &self.dao_creator.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.base_meta_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.quote_usdc_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.initial_liquidity_provider.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident + .airdrop(&self.staker.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.proposer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + } + + pub fn initial_setup(&mut self) { + token::initialize_mint( + &mut self.trident, + self.base_meta_mint_owner.pubkey(), + self.base_meta, + 6, + self.base_meta_mint_owner.pubkey(), + None, + None, + ); + + token::initialize_mint( + &mut self.trident, + self.quote_usdc_mint_owner.pubkey(), + self.quote_usdc, + 6, + self.quote_usdc_mint_owner.pubkey(), + None, + None, + ); + + let alice_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.alice.pubkey(), + ); + let alice_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.alice.pubkey(), + ); + + let bob_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.bob.pubkey(), + ); + let bob_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.bob.pubkey(), + ); + + let staker_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.staker.pubkey(), + ); + + let initial_liquidity_provider_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.initial_liquidity_provider.pubkey(), + ); + let initial_liquidity_provider_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.initial_liquidity_provider.pubkey(), + ); + + token::mint_to( + &mut self.trident, + alice_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + alice_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + bob_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + bob_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + staker_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_STAKE_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + initial_liquidity_provider_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + initial_liquidity_provider_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + ); + } +} diff --git a/trident-tests/fuzz_2/test_fuzz.rs b/trident-tests/fuzz_2/test_fuzz.rs new file mode 100644 index 000000000..d4b12fcb8 --- /dev/null +++ b/trident-tests/fuzz_2/test_fuzz.rs @@ -0,0 +1,264 @@ +use fuzz_accounts::*; +use squads_multisig::client::ProposalCreateArgs; +use squads_multisig::state::SmallVec; +use squads_multisig::state::TransactionMessage; +use trident_fuzz::fuzzing::*; + +#[path = "../common/mod.rs"] +pub mod common; +pub mod methods; + +mod constants; +mod fuzz_accounts; + +mod flows; +mod helpers; +mod invariants; + +use crate::constants::*; +use common::constants::permissionless_account; +use common::types::conditional_vault; +use common::types::futarchy; +use common::types::futarchy::InitializeDaoParams; + +#[derive(Default)] +struct BalanceTracking { + base_underlying_total: u128, + quote_underlying_total: u128, +} + +/// Main fuzz test structure for conditional trading operations. +#[derive(FuzzTestMethods)] +struct FuzzTest { + trident: Trident, + fuzz_accounts: AccountAddresses, + + permissionless_account: Keypair, + alice: Keypair, + bob: Keypair, + staker: Keypair, + proposer: Keypair, + initial_liquidity_provider: Keypair, + payer: Keypair, + dao_creator: Keypair, + base_meta_mint_owner: Keypair, + quote_usdc_mint_owner: Keypair, + + base_meta: Pubkey, + quote_usdc: Pubkey, + dao: Pubkey, + question: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + proposal: Pubkey, + tracking: BalanceTracking, +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + let permissionless_account = permissionless_account(); + let alice = trident.random_keypair(); + let bob = trident.random_keypair(); + let staker = trident.random_keypair(); + let proposer = trident.random_keypair(); + let initial_liquidity_provider = trident.random_keypair(); + let payer = trident.random_keypair(); + let dao_creator = trident.random_keypair(); + let base_meta_mint_owner = trident.random_keypair(); + let quote_usdc_mint_owner = trident.random_keypair(); + let base_meta = trident.random_keypair().pubkey(); + let quote_usdc = trident.random_keypair().pubkey(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + permissionless_account, + alice, + bob, + staker, + proposer, + initial_liquidity_provider, + payer, + dao_creator, + base_meta_mint_owner, + quote_usdc_mint_owner, + base_meta, + quote_usdc, + dao: Pubkey::default(), + question: Pubkey::default(), + base_vault: Pubkey::default(), + quote_vault: Pubkey::default(), + proposal: Pubkey::default(), + tracking: BalanceTracking::default(), + } + } + + /// Initializes DAO, creates and launches a proposal, and transitions AMM to Futarchy state. + #[init] + fn start(&mut self) { + self.airdrop_accounts(); + self.initial_setup(); + + let init_dao_params = InitializeDaoParams::new( + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 86401, + 0, + None, + 0, + self.dao_creator.pubkey(), + ); + + let (dao, multisig) = methods::futarchy::initialize_dao( + &mut self.trident, + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.base_meta, + self.quote_usdc, + init_dao_params, + Some("Initialize DAO for Conditional Trading Fuzz"), + ); + self.dao = dao; + + methods::squads::initialize_vault_transaction( + &mut self.trident, + multisig, + self.permissionless_account.pubkey(), + self.payer.pubkey(), + 0, + 0, + TransactionMessage { + num_signers: 0, + num_writable_signers: 0, + num_writable_non_signers: 0, + account_keys: SmallVec::from(vec![]), + instructions: SmallVec::from(vec![]), + address_table_lookups: SmallVec::from(vec![]), + }, + Some("Initialize Vault Transaction"), + ); + + let squads_proposal = methods::squads::initialize_squads_proposal( + &mut self.trident, + multisig, + self.permissionless_account.pubkey(), + self.payer.pubkey(), + ProposalCreateArgs { + transaction_index: 1, + draft: false, + }, + Some("Initialize Squads Proposal"), + ); + + let oracle = methods::pda::get_proposal_pda(&mut self.trident, squads_proposal); + let question_args = conditional_vault::InitializeQuestionArgs::new([0; 32], oracle, 2); + self.question = methods::conditional_vault::initialize_question( + &mut self.trident, + self.payer.pubkey(), + question_args, + Some("Initialize Question"), + ); + + self.base_vault = methods::conditional_vault::initialize_conditional_vault( + &mut self.trident, + self.payer.pubkey(), + self.question, + self.base_meta, + Some("Initialize Base Conditional Vault"), + ); + self.quote_vault = methods::conditional_vault::initialize_conditional_vault( + &mut self.trident, + self.payer.pubkey(), + self.question, + self.quote_usdc, + Some("Initialize Quote Conditional Vault"), + ); + + self.proposal = methods::futarchy::initialize_proposal( + &mut self.trident, + self.dao, + squads_proposal, + multisig, + self.proposer.pubkey(), + self.payer.pubkey(), + self.question, + self.base_vault, + self.quote_vault, + Some("Initialize Proposal"), + ); + + let proposal_base_ata = methods::token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.proposal, + ); + methods::futarchy::stake_to_proposal( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.proposal, + self.staker.pubkey(), + proposal_base_ata, + futarchy::StakeToProposalParams::new(TEST_INITIAL_STAKE_AMOUNT), + Some("Stake To Proposal"), + ); + + let liq_res = methods::futarchy::add_liqidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.initial_liquidity_provider.pubkey(), + futarchy::ProvideLiquidityParams::new( + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + 0, + self.initial_liquidity_provider.pubkey(), + ), + Some("Initial Spot Liquidity"), + ); + assert!(liq_res.is_success(), "Initial liquidity seed must succeed"); + + methods::futarchy::launch_proposal( + &mut self.trident, + multisig, + squads_proposal, + self.dao, + self.payer.pubkey(), + self.proposal, + self.base_vault, + self.quote_vault, + Some("Launch Proposal"), + ); + + // Ensure setup transitioned the DAO AMM into Futarchy mode. + let _ = self.get_futarchy_pools(); + self.capture_balance_tracking_snapshot(); + } + + #[flow] + fn flow_conditional_swap_alice(&mut self) { + self.conditional_swap_flow(self.alice.pubkey(), "Conditional Swap - Alice"); + } + + #[flow] + fn flow_conditional_swap_bob(&mut self) { + self.conditional_swap_flow(self.bob.pubkey(), "Conditional Swap - Bob"); + } + + #[end] + fn end(&mut self) { + self.assert_global_invariants(); + } +} + +fn main() { + FuzzTest::fuzz(FUZZ_ITERATIONS, FLOWS_PER_ITERATION); +} diff --git a/trident-tests/fuzz_3/constants.rs b/trident-tests/fuzz_3/constants.rs new file mode 100644 index 000000000..832e1ff2e --- /dev/null +++ b/trident-tests/fuzz_3/constants.rs @@ -0,0 +1,18 @@ +// ============================================================================ +// Fuzz Test Configuration +// ============================================================================ +pub const FUZZ_ITERATIONS: u64 = 1000; +pub const FLOWS_PER_ITERATION: u64 = 20; + +// ============================================================================ +// Test Token Amounts +// ============================================================================ +pub const TEST_AIRDROP_SOL: u64 = 5_000_000; +pub const TEST_BASE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; +pub const TEST_QUOTE_INITIAL_AMOUNT: u64 = 1_000_000_000_000_000_000; // USDC has 6 decimals +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT: u64 = 200_000_000_000; +pub const TEST_INITIAL_STAKE_AMOUNT: u64 = 1_000_000; +pub const MIN_TRADE_TIME_FORWARD_SECONDS: i64 = 60; +pub const MAX_TRADE_TIME_FORWARD_SECONDS: i64 = 600; +pub const FINALIZE_TIME_FORWARD_SECONDS: i64 = 90_000; diff --git a/trident-tests/fuzz_3/flows.rs b/trident-tests/fuzz_3/flows.rs new file mode 100644 index 000000000..3372a3e18 --- /dev/null +++ b/trident-tests/fuzz_3/flows.rs @@ -0,0 +1,245 @@ +use crate::common::types::futarchy; +use crate::constants::MAX_TRADE_TIME_FORWARD_SECONDS; +use crate::constants::MIN_TRADE_TIME_FORWARD_SECONDS; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + fn trader_label(&self, trader: Pubkey) -> &'static str { + if trader == self.alice.pubkey() { + "alice" + } else if trader == self.bob.pubkey() { + "bob" + } else { + "unknown" + } + } + + fn swap_type_label(swap_type: &futarchy::SwapType) -> &'static str { + match swap_type { + futarchy::SwapType::Buy => "buy", + futarchy::SwapType::Sell => "sell", + } + } + + fn market_label(market: &futarchy::Market) -> &'static str { + match market { + futarchy::Market::Pass => "pass", + futarchy::Market::Fail => "fail", + futarchy::Market::Spot => "spot", + } + } + + fn forward_random_trade_time(&mut self) { + let random_time_forward_seconds = self + .trident + .random_from_range(MIN_TRADE_TIME_FORWARD_SECONDS..MAX_TRADE_TIME_FORWARD_SECONDS); + self.trident.forward_in_time(random_time_forward_seconds); + } + + /// Random spot swap flow while DAO AMM is in Futarchy state. + pub fn spot_swap_flow(&mut self, trader: Pubkey, label: &str) { + let (spot, _, _) = self.get_futarchy_pools(); + if spot.baseReserves == 0 || spot.quoteReserves == 0 { + return; + } + + let swap_type = if self.trident.random_bool() { + futarchy::SwapType::Buy + } else { + futarchy::SwapType::Sell + }; + + let trader_base_balance = self.get_token_balance(self.base_meta, trader); + let trader_quote_balance = self.get_token_balance(self.quote_usdc, trader); + let max_input = match swap_type { + futarchy::SwapType::Buy => trader_quote_balance.min(spot.quoteReserves), + futarchy::SwapType::Sell => trader_base_balance.min(spot.baseReserves), + }; + if max_input == 0 { + return; + } + + let mut input_amount: u64 = self.trident.random_log_uniform(); + input_amount = input_amount.min(max_input); + if input_amount == 0 { + return; + } + + self.spot_swap_checked(trader, swap_type, input_amount, label); + } + + /// Random conditional swap flow (pass/fail and buy/sell). + pub fn conditional_swap_flow(&mut self, trader: Pubkey, label: &str) { + let market = if self.trident.random_bool() { + futarchy::Market::Pass + } else { + futarchy::Market::Fail + }; + + let swap_type = if self.trident.random_bool() { + futarchy::SwapType::Buy + } else { + futarchy::SwapType::Sell + }; + + if !self.ensure_conditional_input_balance(trader, &market, &swap_type) { + return; + } + + let (input_mint, output_mint) = self.get_market_input_output_mints(&market, &swap_type); + let input_balance = self.get_token_balance(input_mint, trader); + + if input_balance == 0 { + return; + } + + let mut input_amount: u64 = self.trident.random_log_uniform(); + input_amount = input_amount.min(input_balance); + if input_amount == 0 { + return; + } + + self.conditional_swap_checked( + trader, + market, + swap_type, + input_mint, + output_mint, + input_amount, + label, + ); + } + + fn ensure_conditional_input_balance( + &mut self, + trader: Pubkey, + market: &futarchy::Market, + swap_type: &futarchy::SwapType, + ) -> bool { + let (input_mint, _) = self.get_market_input_output_mints(market, swap_type); + if self.get_token_balance(input_mint, trader) > 0 { + return true; + } + + let (vault_to_split, underlying_mint) = match swap_type { + futarchy::SwapType::Buy => (self.quote_vault, self.quote_usdc), + futarchy::SwapType::Sell => (self.base_vault, self.base_meta), + }; + + let underlying_balance = self.get_token_balance(underlying_mint, trader); + if underlying_balance == 0 { + return false; + } + + let mut split_amount: u64 = self.trident.random_log_uniform(); + split_amount = split_amount.min(underlying_balance); + if split_amount == 0 { + return false; + } + + crate::methods::conditional_vault::split_tokens( + &mut self.trident, + self.payer.pubkey(), + self.question, + vault_to_split, + split_amount, + trader, + Some("Prepare conditional input balance"), + ); + + self.get_token_balance(input_mint, trader) > 0 + } + + #[allow(clippy::too_many_arguments)] + fn conditional_swap_checked( + &mut self, + trader: Pubkey, + market: futarchy::Market, + swap_type: futarchy::SwapType, + input_mint: Pubkey, + output_mint: Pubkey, + input_amount: u64, + label: &str, + ) { + let input_before = self.get_token_balance(input_mint, trader); + let output_before = self.get_token_balance(output_mint, trader); + let market_pool_before = self.get_market_pool(&market); + + let res = crate::methods::futarchy::conditional_swap( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.proposal, + trader, + self.question, + self.base_vault, + self.quote_vault, + futarchy::ConditionalSwapParams::new( + market.clone(), + swap_type.clone(), + input_amount, + 0, + ), + Some(label), + ); + + if !res.is_success() { + return; + } + + let metric_name = format!( + "fuzz_3.conditional.{}.{}.{}.input_amount", + self.trader_label(trader), + Self::market_label(&market), + Self::swap_type_label(&swap_type) + ); + self.trident + .record_histogram(&metric_name, input_amount as f64); + + self.check_conditional_swap_invariants( + trader, + market, + swap_type, + input_mint, + output_mint, + input_before, + output_before, + market_pool_before, + ); + self.forward_random_trade_time(); + } + + fn spot_swap_checked( + &mut self, + trader: Pubkey, + swap_type: futarchy::SwapType, + input_amount: u64, + label: &str, + ) { + let base_before = self.get_token_balance(self.base_meta, trader); + let quote_before = self.get_token_balance(self.quote_usdc, trader); + + let res = crate::methods::futarchy::spot_swap( + &mut self.trident, + self.dao, + trader, + futarchy::SpotSwapParams::new(input_amount, swap_type.clone(), 0), + Some(label), + ); + if !res.is_success() { + return; + } + + let metric_name = format!( + "fuzz_3.spot.{}.{}.input_amount", + self.trader_label(trader), + Self::swap_type_label(&swap_type) + ); + self.trident + .record_histogram(&metric_name, input_amount as f64); + + self.check_spot_swap_invariants(trader, swap_type, base_before, quote_before); + self.forward_random_trade_time(); + } +} diff --git a/trident-tests/fuzz_3/fuzz_accounts.rs b/trident-tests/fuzz_3/fuzz_accounts.rs new file mode 100644 index 000000000..48272ddb5 --- /dev/null +++ b/trident-tests/fuzz_3/fuzz_accounts.rs @@ -0,0 +1,10 @@ +#![allow(non_snake_case)] + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses {} diff --git a/trident-tests/fuzz_3/helpers.rs b/trident-tests/fuzz_3/helpers.rs new file mode 100644 index 000000000..ace680949 --- /dev/null +++ b/trident-tests/fuzz_3/helpers.rs @@ -0,0 +1,105 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::types::futarchy; + +impl FuzzTest { + /// Gets token balance for a given mint and owner. + pub fn get_token_balance(&mut self, mint: Pubkey, owner: Pubkey) -> u64 { + let ata = self + .trident + .get_associated_token_address(&mint, &owner, &TOKEN_PROGRAM_ID); + + match self.trident.get_token_account(ata) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + /// Returns spot/pass/fail pools from the DAO in Futarchy mode. + pub fn get_futarchy_pools(&mut self) -> (futarchy::Pool, futarchy::Pool, futarchy::Pool) { + let dao_data = self + .trident + .get_account_with_type::(&self.dao, None) + .expect("Dao not found"); + + match dao_data.amm.state { + futarchy::PoolState::Futarchy { spot, pass, fail } => (spot, pass, fail), + futarchy::PoolState::Spot { .. } => { + panic!("Conditional trading fuzz expects DAO AMM to be in Futarchy state") + } + } + } + + /// Gets proposal conditional token mints (pass base/quote, fail base/quote). + pub fn get_conditional_mints(&mut self) -> (Pubkey, Pubkey, Pubkey, Pubkey) { + let proposal_data = self + .trident + .get_account_with_type::(&self.proposal, None) + .expect("Proposal not found"); + + ( + proposal_data.passBaseMint, + proposal_data.passQuoteMint, + proposal_data.failBaseMint, + proposal_data.failQuoteMint, + ) + } + + pub fn get_market_pool(&mut self, market: &futarchy::Market) -> futarchy::Pool { + let (_, pass, fail) = self.get_futarchy_pools(); + match market { + futarchy::Market::Pass => pass, + futarchy::Market::Fail => fail, + futarchy::Market::Spot => panic!("Conditional trading fuzz only supports pass/fail"), + } + } + + pub fn get_market_input_output_mints( + &mut self, + market: &futarchy::Market, + swap_type: &futarchy::SwapType, + ) -> (Pubkey, Pubkey) { + let (pass_base_mint, pass_quote_mint, fail_base_mint, fail_quote_mint) = + self.get_conditional_mints(); + + match (market, swap_type) { + (futarchy::Market::Pass, futarchy::SwapType::Buy) => (pass_quote_mint, pass_base_mint), + (futarchy::Market::Pass, futarchy::SwapType::Sell) => (pass_base_mint, pass_quote_mint), + (futarchy::Market::Fail, futarchy::SwapType::Buy) => (fail_quote_mint, fail_base_mint), + (futarchy::Market::Fail, futarchy::SwapType::Sell) => (fail_base_mint, fail_quote_mint), + (futarchy::Market::Spot, _) => { + panic!("Conditional trading fuzz only supports pass/fail") + } + } + } + + pub fn get_token_account_balance(&mut self, token_account: Pubkey) -> u64 { + match self.trident.get_token_account(token_account) { + Ok(account) => account.account.amount, + Err(_) => 0, + } + } + + pub fn get_underlying_vault_accounts(&mut self) -> (Pubkey, Pubkey) { + let base_vault_data = self + .trident + .get_account_with_type::( + &self.base_vault, + None, + ) + .expect("Base conditional vault not found"); + let quote_vault_data = self + .trident + .get_account_with_type::( + &self.quote_vault, + None, + ) + .expect("Quote conditional vault not found"); + ( + base_vault_data.underlyingTokenAccount, + quote_vault_data.underlyingTokenAccount, + ) + } +} diff --git a/trident-tests/fuzz_3/invariants.rs b/trident-tests/fuzz_3/invariants.rs new file mode 100644 index 000000000..1bc5b8734 --- /dev/null +++ b/trident-tests/fuzz_3/invariants.rs @@ -0,0 +1,336 @@ +use crate::common::types::futarchy; +use crate::constants::FINALIZE_TIME_FORWARD_SECONDS; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + pub fn capture_balance_tracking_snapshot(&mut self) { + self.tracking.base_underlying_total = self.current_total_base_underlying(); + self.tracking.quote_underlying_total = self.current_total_quote_underlying(); + } + + fn current_total_base_underlying(&mut self) -> u128 { + let (base_underlying_vault_ata, _) = self.get_underlying_vault_accounts(); + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + owners + .iter() + .map(|owner| self.get_token_balance(self.base_meta, *owner) as u128) + .sum::() + + self.get_token_account_balance(base_underlying_vault_ata) as u128 + } + + fn current_total_quote_underlying(&mut self) -> u128 { + let (_, quote_underlying_vault_ata) = self.get_underlying_vault_accounts(); + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + owners + .iter() + .map(|owner| self.get_token_balance(self.quote_usdc, *owner) as u128) + .sum::() + + self.get_token_account_balance(quote_underlying_vault_ata) as u128 + } + + fn current_total_conditional_pair_for_owner_set( + &mut self, + pass_mint: Pubkey, + fail_mint: Pubkey, + ) -> (u128, u128) { + let owners = [ + self.dao, + self.alice.pubkey(), + self.bob.pubkey(), + self.staker.pubkey(), + self.initial_liquidity_provider.pubkey(), + self.proposer.pubkey(), + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.proposal, + ]; + let pass_total = owners + .iter() + .map(|owner| self.get_token_balance(pass_mint, *owner) as u128) + .sum(); + let fail_total = owners + .iter() + .map(|owner| self.get_token_balance(fail_mint, *owner) as u128) + .sum(); + (pass_total, fail_total) + } + + #[allow(clippy::too_many_arguments)] + pub fn check_conditional_swap_invariants( + &mut self, + trader: Pubkey, + market: futarchy::Market, + swap_type: futarchy::SwapType, + input_mint: Pubkey, + output_mint: Pubkey, + input_before: u64, + output_before: u64, + market_pool_before: futarchy::Pool, + ) { + let input_after = self.get_token_balance(input_mint, trader); + let output_after = self.get_token_balance(output_mint, trader); + let market_pool_after = self.get_market_pool(&market); + + assert!( + input_after <= input_before, + "Trader input token balance must not increase after swap" + ); + assert!( + output_after >= output_before, + "Trader output token balance must not decrease after swap" + ); + + match swap_type { + futarchy::SwapType::Buy => { + assert!( + market_pool_after.quoteReserves >= market_pool_before.quoteReserves, + "Buy in conditional market should increase quote reserves" + ); + assert!( + market_pool_after.baseReserves <= market_pool_before.baseReserves, + "Buy in conditional market should decrease base reserves" + ); + } + futarchy::SwapType::Sell => { + assert!( + market_pool_after.baseReserves >= market_pool_before.baseReserves, + "Sell in conditional market should increase base reserves" + ); + assert!( + market_pool_after.quoteReserves <= market_pool_before.quoteReserves, + "Sell in conditional market should decrease quote reserves" + ); + } + } + } + + pub fn check_spot_swap_invariants( + &mut self, + trader: Pubkey, + swap_type: futarchy::SwapType, + base_before: u64, + quote_before: u64, + ) { + let base_after = self.get_token_balance(self.base_meta, trader); + let quote_after = self.get_token_balance(self.quote_usdc, trader); + + match swap_type { + futarchy::SwapType::Buy => { + assert!( + base_after >= base_before, + "Spot buy: trader base balance should increase or stay same" + ); + assert!( + quote_after <= quote_before, + "Spot buy: trader quote balance should decrease or stay same" + ); + } + futarchy::SwapType::Sell => { + assert!( + base_after <= base_before, + "Spot sell: trader base balance should decrease or stay same" + ); + assert!( + quote_after >= quote_before, + "Spot sell: trader quote balance should increase or stay same" + ); + } + } + } + + pub fn assert_global_invariants(&mut self) { + self.assert_futarchy_vault_alignment(); + + let (pass_base_mint, pass_quote_mint, fail_base_mint, fail_quote_mint) = + self.get_conditional_mints(); + + assert_eq!( + self.current_total_base_underlying(), + self.tracking.base_underlying_total, + "Base underlying total should be conserved across known holders" + ); + assert_eq!( + self.current_total_quote_underlying(), + self.tracking.quote_underlying_total, + "Quote underlying total should be conserved across known holders" + ); + + let (pass_base_total, fail_base_total) = + self.current_total_conditional_pair_for_owner_set(pass_base_mint, fail_base_mint); + assert_eq!( + pass_base_total, fail_base_total, + "Pass/fail base conditional totals should remain equal over tracked owners" + ); + + let (pass_quote_total, fail_quote_total) = + self.current_total_conditional_pair_for_owner_set(pass_quote_mint, fail_quote_mint); + assert_eq!( + pass_quote_total, fail_quote_total, + "Pass/fail quote conditional totals should remain equal over tracked owners" + ); + } + + pub fn finalize_proposal_and_assert_spot_alignment(&mut self) { + let proposal_data = self + .trident + .get_account_with_type::(&self.proposal, None) + .expect("Proposal not found"); + if !matches!(proposal_data.state, futarchy::ProposalState::Pending) { + return; + } + + self.trident.forward_in_time(FINALIZE_TIME_FORWARD_SECONDS); + self.crank_twap_for_finalize(); + + let res = crate::methods::futarchy::finalize_proposal( + &mut self.trident, + self.dao, + self.proposal, + self.question, + self.base_vault, + self.quote_vault, + Some("Finalize Proposal"), + ); + assert!( + res.is_success(), + "Finalize proposal must succeed,failed: {}", + res.logs() + ); + + let proposal_data = self + .trident + .get_account_with_type::(&self.proposal, None) + .expect("Proposal not found after finalize"); + assert!( + matches!( + proposal_data.state, + futarchy::ProposalState::Passed | futarchy::ProposalState::Failed + ), + "Proposal should be finalized to Passed or Failed" + ); + + let dao_data = self + .trident + .get_account_with_type::(&self.dao, None) + .expect("Dao not found after finalize"); + let futarchy::PoolState::Spot { spot } = dao_data.amm.state else { + panic!("DAO AMM must transition to Spot after finalization"); + }; + + let dao_spot_base_balance = self.get_token_balance(self.base_meta, self.dao); + let dao_spot_quote_balance = self.get_token_balance(self.quote_usdc, self.dao); + + assert_eq!( + spot.baseReserves as u128 + spot.baseProtocolFeeBalance as u128, + dao_spot_base_balance as u128, + "Post-finalize spot base vault must align with spot reserves+fees" + ); + assert_eq!( + spot.quoteReserves as u128 + spot.quoteProtocolFeeBalance as u128, + dao_spot_quote_balance as u128, + "Post-finalize spot quote vault must align with spot reserves+fees" + ); + } + + fn assert_futarchy_vault_alignment(&mut self) { + let (spot, pass, fail) = self.get_futarchy_pools(); + + let (pass_base_mint, pass_quote_mint, fail_base_mint, fail_quote_mint) = + self.get_conditional_mints(); + + let dao_spot_base_balance = self.get_token_balance(self.base_meta, self.dao) as u128; + let dao_spot_quote_balance = self.get_token_balance(self.quote_usdc, self.dao) as u128; + let dao_pass_base_balance = self.get_token_balance(pass_base_mint, self.dao) as u128; + let dao_pass_quote_balance = self.get_token_balance(pass_quote_mint, self.dao) as u128; + let dao_fail_base_balance = self.get_token_balance(fail_base_mint, self.dao) as u128; + let dao_fail_quote_balance = self.get_token_balance(fail_quote_mint, self.dao) as u128; + + let spot_base_state = spot.baseReserves as u128 + spot.baseProtocolFeeBalance as u128; + let spot_quote_state = spot.quoteReserves as u128 + spot.quoteProtocolFeeBalance as u128; + let pass_base_state = pass.baseReserves as u128 + pass.baseProtocolFeeBalance as u128; + let pass_quote_state = pass.quoteReserves as u128 + pass.quoteProtocolFeeBalance as u128; + let fail_base_state = fail.baseReserves as u128 + fail.baseProtocolFeeBalance as u128; + let fail_quote_state = fail.quoteReserves as u128 + fail.quoteProtocolFeeBalance as u128; + + // In Futarchy mode, spot underlying can move together with pass/fail legs through + // split/merge mechanics, so we validate alignment on aggregated leg pairs. + assert_eq!( + spot_base_state + pass_base_state, + dao_spot_base_balance + dao_pass_base_balance, + "Vault/reserve mismatch for spot+pass base leg" + ); + assert_eq!( + spot_base_state + fail_base_state, + dao_spot_base_balance + dao_fail_base_balance, + "Vault/reserve mismatch for spot+fail base leg" + ); + assert_eq!( + spot_quote_state + pass_quote_state, + dao_spot_quote_balance + dao_pass_quote_balance, + "Vault/reserve mismatch for spot+pass quote leg" + ); + assert_eq!( + spot_quote_state + fail_quote_state, + dao_spot_quote_balance + dao_fail_quote_balance, + "Vault/reserve mismatch for spot+fail quote leg" + ); + } + + fn crank_twap_for_finalize(&mut self) { + let (spot, _, _) = self.get_futarchy_pools(); + if spot.baseReserves == 0 || spot.quoteReserves == 0 { + return; + } + + for trader in [self.alice.pubkey(), self.bob.pubkey()] { + let quote_balance = self.get_token_balance(self.quote_usdc, trader); + if quote_balance > 0 { + let res = crate::methods::futarchy::spot_swap( + &mut self.trident, + self.dao, + trader, + futarchy::SpotSwapParams::new(1, futarchy::SwapType::Buy, 0), + Some("TWAP crank before finalize"), + ); + if res.is_success() { + return; + } + } + + let base_balance = self.get_token_balance(self.base_meta, trader); + if base_balance > 0 { + let res = crate::methods::futarchy::spot_swap( + &mut self.trident, + self.dao, + trader, + futarchy::SpotSwapParams::new(1, futarchy::SwapType::Sell, 0), + Some("TWAP crank before finalize"), + ); + if res.is_success() { + return; + } + } + } + } +} diff --git a/trident-tests/fuzz_3/methods.rs b/trident-tests/fuzz_3/methods.rs new file mode 100644 index 000000000..56e62d2dc --- /dev/null +++ b/trident-tests/fuzz_3/methods.rs @@ -0,0 +1,162 @@ +use crate::constants::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +// Re-export common modules for convenience +pub use crate::common::conditional_vault; +pub use crate::common::futarchy; +pub use crate::common::pda; +pub use crate::common::squads; +pub use crate::common::token; + +impl FuzzTest { + pub fn airdrop_accounts(&mut self) { + self.trident + .airdrop(&self.alice.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.bob.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.payer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident.airdrop( + &self.dao_creator.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.base_meta_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.quote_usdc_mint_owner.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident.airdrop( + &self.initial_liquidity_provider.pubkey(), + TEST_AIRDROP_SOL * LAMPORTS_PER_SOL, + ); + self.trident + .airdrop(&self.staker.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + self.trident + .airdrop(&self.proposer.pubkey(), TEST_AIRDROP_SOL * LAMPORTS_PER_SOL); + } + + pub fn initial_setup(&mut self) { + token::initialize_mint( + &mut self.trident, + self.base_meta_mint_owner.pubkey(), + self.base_meta, + 6, + self.base_meta_mint_owner.pubkey(), + None, + None, + ); + + token::initialize_mint( + &mut self.trident, + self.quote_usdc_mint_owner.pubkey(), + self.quote_usdc, + 6, + self.quote_usdc_mint_owner.pubkey(), + None, + None, + ); + + let alice_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.alice.pubkey(), + ); + let alice_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.alice.pubkey(), + ); + + let bob_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.bob.pubkey(), + ); + let bob_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.bob.pubkey(), + ); + + let staker_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.staker.pubkey(), + ); + + let initial_liquidity_provider_base_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.initial_liquidity_provider.pubkey(), + ); + let initial_liquidity_provider_quote_ata = token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.quote_usdc, + self.initial_liquidity_provider.pubkey(), + ); + + token::mint_to( + &mut self.trident, + alice_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + alice_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + bob_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + bob_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_QUOTE_INITIAL_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + staker_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_STAKE_AMOUNT, + ); + + token::mint_to( + &mut self.trident, + initial_liquidity_provider_base_ata, + self.base_meta, + self.base_meta_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + ); + token::mint_to( + &mut self.trident, + initial_liquidity_provider_quote_ata, + self.quote_usdc, + self.quote_usdc_mint_owner.pubkey(), + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + ); + } +} diff --git a/trident-tests/fuzz_3/test_fuzz.rs b/trident-tests/fuzz_3/test_fuzz.rs new file mode 100644 index 000000000..ad9bde1cb --- /dev/null +++ b/trident-tests/fuzz_3/test_fuzz.rs @@ -0,0 +1,275 @@ +use fuzz_accounts::*; +use squads_multisig::client::ProposalCreateArgs; +use squads_multisig::state::SmallVec; +use squads_multisig::state::TransactionMessage; +use trident_fuzz::fuzzing::*; + +#[path = "../common/mod.rs"] +pub mod common; +pub mod methods; + +mod constants; +mod fuzz_accounts; + +mod flows; +mod helpers; +mod invariants; + +use crate::constants::*; +use common::constants::permissionless_account; +use common::types::conditional_vault; +use common::types::futarchy; +use common::types::futarchy::InitializeDaoParams; + +#[derive(Default)] +struct BalanceTracking { + base_underlying_total: u128, + quote_underlying_total: u128, +} + +/// Main fuzz test structure for mixed spot + conditional trading operations. +#[derive(FuzzTestMethods)] +struct FuzzTest { + trident: Trident, + fuzz_accounts: AccountAddresses, + + permissionless_account: Keypair, + alice: Keypair, + bob: Keypair, + staker: Keypair, + proposer: Keypair, + initial_liquidity_provider: Keypair, + payer: Keypair, + dao_creator: Keypair, + base_meta_mint_owner: Keypair, + quote_usdc_mint_owner: Keypair, + + base_meta: Pubkey, + quote_usdc: Pubkey, + dao: Pubkey, + question: Pubkey, + base_vault: Pubkey, + quote_vault: Pubkey, + proposal: Pubkey, + tracking: BalanceTracking, +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + let permissionless_account = permissionless_account(); + let alice = trident.random_keypair(); + let bob = trident.random_keypair(); + let staker = trident.random_keypair(); + let proposer = trident.random_keypair(); + let initial_liquidity_provider = trident.random_keypair(); + let payer = trident.random_keypair(); + let dao_creator = trident.random_keypair(); + let base_meta_mint_owner = trident.random_keypair(); + let quote_usdc_mint_owner = trident.random_keypair(); + let base_meta = trident.random_keypair().pubkey(); + let quote_usdc = trident.random_keypair().pubkey(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + permissionless_account, + alice, + bob, + staker, + proposer, + initial_liquidity_provider, + payer, + dao_creator, + base_meta_mint_owner, + quote_usdc_mint_owner, + base_meta, + quote_usdc, + dao: Pubkey::default(), + question: Pubkey::default(), + base_vault: Pubkey::default(), + quote_vault: Pubkey::default(), + proposal: Pubkey::default(), + tracking: BalanceTracking::default(), + } + } + + /// Initializes DAO, creates and launches a proposal, and transitions AMM to Futarchy state. + #[init] + fn start(&mut self) { + self.airdrop_accounts(); + self.initial_setup(); + + let init_dao_params = InitializeDaoParams::new( + 1, + u32::MAX as u128, + 0, + 1, + 1, + 0, + 0, + 86401, + 0, + None, + 0, + self.dao_creator.pubkey(), + ); + + let (dao, multisig) = methods::futarchy::initialize_dao( + &mut self.trident, + self.payer.pubkey(), + self.dao_creator.pubkey(), + self.base_meta, + self.quote_usdc, + init_dao_params, + Some("Initialize DAO for Mixed Trading Fuzz"), + ); + self.dao = dao; + + methods::squads::initialize_vault_transaction( + &mut self.trident, + multisig, + self.permissionless_account.pubkey(), + self.payer.pubkey(), + 0, + 0, + TransactionMessage { + num_signers: 0, + num_writable_signers: 0, + num_writable_non_signers: 0, + account_keys: SmallVec::from(vec![]), + instructions: SmallVec::from(vec![]), + address_table_lookups: SmallVec::from(vec![]), + }, + Some("Initialize Vault Transaction"), + ); + + let squads_proposal = methods::squads::initialize_squads_proposal( + &mut self.trident, + multisig, + self.permissionless_account.pubkey(), + self.payer.pubkey(), + ProposalCreateArgs { + transaction_index: 1, + draft: false, + }, + Some("Initialize Squads Proposal"), + ); + + let oracle = methods::pda::get_proposal_pda(&mut self.trident, squads_proposal); + let question_args = conditional_vault::InitializeQuestionArgs::new([0; 32], oracle, 2); + self.question = methods::conditional_vault::initialize_question( + &mut self.trident, + self.payer.pubkey(), + question_args, + Some("Initialize Question"), + ); + + self.base_vault = methods::conditional_vault::initialize_conditional_vault( + &mut self.trident, + self.payer.pubkey(), + self.question, + self.base_meta, + Some("Initialize Base Conditional Vault"), + ); + self.quote_vault = methods::conditional_vault::initialize_conditional_vault( + &mut self.trident, + self.payer.pubkey(), + self.question, + self.quote_usdc, + Some("Initialize Quote Conditional Vault"), + ); + + self.proposal = methods::futarchy::initialize_proposal( + &mut self.trident, + self.dao, + squads_proposal, + multisig, + self.proposer.pubkey(), + self.payer.pubkey(), + self.question, + self.base_vault, + self.quote_vault, + Some("Initialize Proposal"), + ); + + let proposal_base_ata = methods::token::initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.base_meta, + self.proposal, + ); + methods::futarchy::stake_to_proposal( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.proposal, + self.staker.pubkey(), + proposal_base_ata, + futarchy::StakeToProposalParams::new(TEST_INITIAL_STAKE_AMOUNT), + Some("Stake To Proposal"), + ); + + let liq_res = methods::futarchy::add_liqidity( + &mut self.trident, + self.dao, + self.payer.pubkey(), + self.initial_liquidity_provider.pubkey(), + futarchy::ProvideLiquidityParams::new( + TEST_INITIAL_LIQUIDITY_PROVIDER_QUOTE_INITIAL_AMOUNT, + TEST_INITIAL_LIQUIDITY_PROVIDER_BASE_INITIAL_AMOUNT, + 0, + self.initial_liquidity_provider.pubkey(), + ), + Some("Initial Spot Liquidity"), + ); + assert!(liq_res.is_success(), "Initial liquidity seed must succeed"); + + methods::futarchy::launch_proposal( + &mut self.trident, + multisig, + squads_proposal, + self.dao, + self.payer.pubkey(), + self.proposal, + self.base_vault, + self.quote_vault, + Some("Launch Proposal"), + ); + + // Ensure setup transitioned the DAO AMM into Futarchy mode. + let _ = self.get_futarchy_pools(); + self.capture_balance_tracking_snapshot(); + } + + #[flow] + fn flow_conditional_swap_alice(&mut self) { + self.conditional_swap_flow(self.alice.pubkey(), "Conditional Swap - Alice"); + } + + #[flow] + fn flow_conditional_swap_bob(&mut self) { + self.conditional_swap_flow(self.bob.pubkey(), "Conditional Swap - Bob"); + } + + #[flow] + fn flow_spot_swap_alice(&mut self) { + self.spot_swap_flow(self.alice.pubkey(), "Spot Swap - Alice"); + } + + #[flow] + fn flow_spot_swap_bob(&mut self) { + self.spot_swap_flow(self.bob.pubkey(), "Spot Swap - Bob"); + } + + #[end] + fn end(&mut self) { + self.assert_global_invariants(); + self.finalize_proposal_and_assert_spot_alignment(); + } +} + +fn main() { + FuzzTest::fuzz(FUZZ_ITERATIONS, FLOWS_PER_ITERATION); +} diff --git a/trident-tests/fuzz_launchpad/fuzz_accounts.rs b/trident-tests/fuzz_launchpad/fuzz_accounts.rs new file mode 100644 index 000000000..ea722d6be --- /dev/null +++ b/trident-tests/fuzz_launchpad/fuzz_accounts.rs @@ -0,0 +1,31 @@ +#![allow(non_snake_case)] +use trident_fuzz::fuzzing::*; + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses { + pub launch: AddressStorage, + + pub baseMint: AddressStorage, + + pub tokenMetadata: AddressStorage, + + pub launchSigner: AddressStorage, + + pub launchAuthority: AddressStorage, + + pub quoteMint: AddressStorage, + + pub fundingRecord: AddressStorage, + + pub funder: AddressStorage, + + pub additionalTokensRecipient: AddressStorage, + + pub feeRecipient: AddressStorage, +} diff --git a/trident-tests/fuzz_launchpad/invariants/claim.rs b/trident-tests/fuzz_launchpad/invariants/claim.rs new file mode 100644 index 000000000..9fa237a11 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/claim.rs @@ -0,0 +1,168 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::FUNDING_RECORD_SEED_PREFIX; +use crate::common::types::launchpad_v_7::FundingRecord; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::common::types::launchpad_v_7::{self}; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +// Keep in sync with `programs/v07_launchpad/src/lib.rs`. +const TOKEN_SCALE: u64 = 1_000_000; +const TOKENS_TO_PARTICIPANTS: u64 = 10_000_000 * TOKEN_SCALE; + +impl FuzzTest { + pub fn verify_claim_invariants( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_signer: Pubkey, + base_mint: Pubkey, + launch_base_vault: Pubkey, + funder: Pubkey, + funder_token_account: Pubkey, + pre_launch: &Launch, + pre_funding_record: &FundingRecord, + pre_vault_amount: u64, + pre_funder_token_amount: u64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after Claim"); + let post_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .expect("FundingRecord must exist after Claim"); + + // Invariant 1: Must be Complete, and tokens not previously claimed, if tx succeeded. + assert!( + matches!(pre_launch.state, LaunchState::Complete), + "pre-state must be Complete if Claim succeeded" + ); + assert!( + !pre_funding_record.isTokensClaimed, + "pre FundingRecord.isTokensClaimed must be false if Claim succeeded" + ); + + // Invariant 2: `launch` has_one constraints must hold. + assert_eq!( + pre_launch.launchSigner, launch_signer, + "launch_signer must match Launch.launchSigner" + ); + assert_eq!( + pre_launch.baseMint, base_mint, + "base_mint must match Launch.baseMint" + ); + assert_eq!( + pre_launch.launchBaseVault, launch_base_vault, + "launch_base_vault must match Launch.launchBaseVault" + ); + + // Invariant 3: FundingRecord PDA + has_one funder must hold. + let (expected_fr_pda, _) = self.trident.find_program_address( + &[FUNDING_RECORD_SEED_PREFIX, launch.as_ref(), funder.as_ref()], + &launchpad_v_7::program_id(), + ); + assert_eq!( + funding_record, expected_fr_pda, + "FundingRecord PDA must match seeds (funding_record, launch, funder)" + ); + assert_eq!( + pre_funding_record.funder, funder, + "FundingRecord.funder must match provided funder" + ); + assert_eq!( + pre_funding_record.launch, launch, + "FundingRecord.launch must equal launch" + ); + + // Invariant 4: Token amount computation must match approvedAmount/totalApprovedAmount * TOKENS_TO_PARTICIPANTS. + assert!( + pre_launch.totalApprovedAmount > 0, + "totalApprovedAmount must be > 0 if Claim succeeded" + ); + + let expected_token_amount_u128 = (pre_funding_record.approvedAmount as u128) + .checked_mul(TOKENS_TO_PARTICIPANTS as u128) + .expect("token_amount multiplication overflow should be impossible") + .checked_div(pre_launch.totalApprovedAmount as u128) + .expect("division by zero should be impossible"); + assert!( + expected_token_amount_u128 <= u64::MAX as u128, + "expected token amount must fit in u64" + ); + let expected_token_amount = expected_token_amount_u128 as u64; + + // Invariant 5: FundingRecord mutation must flip isTokensClaimed and preserve other fields. + assert!( + post_funding_record.isTokensClaimed, + "FundingRecord.isTokensClaimed must be true after Claim" + ); + assert_eq!( + post_funding_record.committedAmount, pre_funding_record.committedAmount, + "committedAmount must not change" + ); + assert_eq!( + post_funding_record.approvedAmount, pre_funding_record.approvedAmount, + "approvedAmount must not change" + ); + assert_eq!( + post_funding_record.isUsdcRefunded, pre_funding_record.isUsdcRefunded, + "isUsdcRefunded must not change" + ); + + // Invariant 6: Token accounting must transfer expected_token_amount (vault -> funder). + let post_vault_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault token account must exist after Claim") + .account + .amount; + let post_funder_token_amount = self + .trident + .get_token_account(funder_token_account) + .expect("funder_token_account must exist after Claim") + .account + .amount; + + let vault_delta = pre_vault_amount + .checked_sub(post_vault_amount) + .expect("vault amount must not increase during Claim"); + let funder_delta = post_funder_token_amount + .checked_sub(pre_funder_token_amount) + .expect("funder token amount must not decrease during Claim"); + + assert_eq!( + vault_delta, expected_token_amount, + "vault must decrease by expected_token_amount" + ); + assert_eq!( + funder_delta, expected_token_amount, + "funder must receive expected_token_amount" + ); + + // Invariant 7: Launch mutation must increment seqNum by 1 and keep state Complete. + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on Claim" + ); + match (&post_launch.state, &pre_launch.state) { + (LaunchState::Complete, LaunchState::Complete) => {} + _ => panic!("Launch.state must remain Complete during Claim"), + } + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/claim_additional_token_allocation.rs b/trident-tests/fuzz_launchpad/invariants/claim_additional_token_allocation.rs new file mode 100644 index 000000000..289087c24 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/claim_additional_token_allocation.rs @@ -0,0 +1,127 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_claim_additional_token_allocation_invariants( + &mut self, + launch: Pubkey, + launch_signer: Pubkey, + base_vault: Pubkey, + base_mint: Pubkey, + additional_tokens_recipient: Pubkey, + additional_tokens_recipient_token_account: Pubkey, + pre_launch: &Launch, + pre_base_vault_amount: u64, + pre_recipient_amount: u64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after ClaimAdditionalTokenAllocation"); + + // Invariant 1: Must be Complete, and additional tokens not previously claimed, if tx succeeded. + assert!( + matches!(pre_launch.state, LaunchState::Complete), + "pre-state must be Complete if ClaimAdditionalTokenAllocation succeeded" + ); + assert!( + !pre_launch.additionalTokensClaimed, + "pre additionalTokensClaimed must be false if ClaimAdditionalTokenAllocation succeeded" + ); + assert!( + pre_launch.additionalTokensRecipient.is_some(), + "additionalTokensRecipient must be Some if ClaimAdditionalTokenAllocation succeeded" + ); + assert_eq!( + pre_launch.additionalTokensRecipient.unwrap(), + additional_tokens_recipient, + "additional_tokens_recipient must match Launch.additionalTokensRecipient" + ); + + // Invariant 2: `launch` has_one constraints must hold. + assert_eq!( + pre_launch.launchSigner, launch_signer, + "launch_signer must match Launch.launchSigner" + ); + assert_eq!( + pre_launch.baseMint, base_mint, + "base_mint must match Launch.baseMint" + ); + assert_eq!( + pre_launch.launchBaseVault, base_vault, + "base_vault must match Launch.launchBaseVault" + ); + + // Invariant 3: additional_tokens_recipient_token_account must be ATA(base_mint, additional_tokens_recipient). + let expected_ata = self.trident.get_associated_token_address( + &base_mint, + &additional_tokens_recipient, + &TOKEN_PROGRAM_ID, + ); + assert_eq!( + additional_tokens_recipient_token_account, expected_ata, + "additional_tokens_recipient_token_account must be ATA(base_mint, additional_tokens_recipient)" + ); + + // Invariant 4: Token accounting must transfer additionalTokensAmount (base_vault -> recipient). + let post_base_vault_amount = self + .trident + .get_token_account(base_vault) + .expect("base_vault token account must exist after ClaimAdditionalTokenAllocation") + .account + .amount; + let post_recipient_amount = self + .trident + .get_token_account(additional_tokens_recipient_token_account) + .expect("recipient token account must exist after ClaimAdditionalTokenAllocation") + .account + .amount; + + let vault_delta = pre_base_vault_amount + .checked_sub(post_base_vault_amount) + .expect("base_vault amount must not increase during ClaimAdditionalTokenAllocation"); + let recipient_delta = post_recipient_amount + .checked_sub(pre_recipient_amount) + .expect("recipient amount must not decrease during ClaimAdditionalTokenAllocation"); + + assert_eq!( + vault_delta, pre_launch.additionalTokensAmount, + "base_vault must decrease by additionalTokensAmount" + ); + assert_eq!( + recipient_delta, pre_launch.additionalTokensAmount, + "recipient must receive additionalTokensAmount" + ); + + // Invariant 5: Launch mutation must flip additionalTokensClaimed and increment seqNum by 1. + assert!( + post_launch.additionalTokensClaimed, + "additionalTokensClaimed must be true after ClaimAdditionalTokenAllocation" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1" + ); + match (&post_launch.state, &pre_launch.state) { + (LaunchState::Complete, LaunchState::Complete) => {} + _ => panic!("Launch.state must remain Complete"), + } + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/close_launch.rs b/trident-tests/fuzz_launchpad/invariants/close_launch.rs new file mode 100644 index 000000000..a79751132 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/close_launch.rs @@ -0,0 +1,177 @@ +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_close_launch_invariants( + &mut self, + launch: Pubkey, + pre_launch: &Launch, + timestamp_before_tx: i64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after CloseLaunch"); + + // Invariant 1: Must be Live pre-state if tx succeeded. + assert!( + matches!(pre_launch.state, LaunchState::Live), + "pre-state must be Live if CloseLaunch succeeded" + ); + + // Invariant 2: Launch period must be over if tx succeeded. + let started = pre_launch + .unixTimestampStarted + .expect("unixTimestampStarted must be Some if CloseLaunch succeeded"); + let period_end = started.saturating_add(pre_launch.secondsForLaunch as i64); + let timestamp_after_tx = self.trident.get_current_timestamp(); + assert!( + timestamp_before_tx >= period_end, + "CloseLaunch must occur after launch period end (timestamp_before_tx)" + ); + assert!( + timestamp_after_tx >= period_end, + "CloseLaunch must occur after launch period end (timestamp_after_tx)" + ); + + // Invariant 3: State transition depends on whether minimum raise was met. + let expected_state = if pre_launch.minimumRaiseAmount > pre_launch.totalCommittedAmount { + LaunchState::Refunding + } else { + LaunchState::Closed + }; + match (&post_launch.state, expected_state) { + (LaunchState::Refunding, LaunchState::Refunding) + | (LaunchState::Closed, LaunchState::Closed) => {} + _ => panic!("post-state must be Refunding or Closed as determined by pre-state totals"), + } + + // Invariant 4: unixTimestampClosed must be set within [timestamp_before_tx, timestamp_after_tx]. + let closed = post_launch + .unixTimestampClosed + .expect("unixTimestampClosed must be Some after CloseLaunch"); + assert!( + closed >= timestamp_before_tx, + "unixTimestampClosed must be >= timestamp_before_tx" + ); + assert!( + closed <= timestamp_after_tx, + "unixTimestampClosed must be <= timestamp_after_tx" + ); + + // Invariant 5: seqNum must increment by exactly 1. + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on CloseLaunch" + ); + + // Invariant 6: Unrelated fields must not change. + assert_eq!( + post_launch.pdaBump, pre_launch.pdaBump, + "pdaBump must not change" + ); + assert_eq!( + post_launch.minimumRaiseAmount, pre_launch.minimumRaiseAmount, + "minimumRaiseAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, pre_launch.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, pre_launch.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must not change" + ); + assert_eq!( + post_launch.launchAuthority, pre_launch.launchAuthority, + "launchAuthority must not change" + ); + assert_eq!( + post_launch.launchSigner, pre_launch.launchSigner, + "launchSigner must not change" + ); + assert_eq!( + post_launch.launchSignerPdaBump, pre_launch.launchSignerPdaBump, + "launchSignerPdaBump must not change" + ); + assert_eq!( + post_launch.launchQuoteVault, pre_launch.launchQuoteVault, + "launchQuoteVault must not change" + ); + assert_eq!( + post_launch.launchBaseVault, pre_launch.launchBaseVault, + "launchBaseVault must not change" + ); + assert_eq!( + post_launch.baseMint, pre_launch.baseMint, + "baseMint must not change" + ); + assert_eq!( + post_launch.quoteMint, pre_launch.quoteMint, + "quoteMint must not change" + ); + assert_eq!( + post_launch.unixTimestampStarted, pre_launch.unixTimestampStarted, + "unixTimestampStarted must not change" + ); + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + assert_eq!( + post_launch.secondsForLaunch, pre_launch.secondsForLaunch, + "secondsForLaunch must not change" + ); + assert_eq!(post_launch.dao, pre_launch.dao, "dao must not change"); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not change" + ); + assert_eq!( + post_launch.performancePackageGrantee, pre_launch.performancePackageGrantee, + "performancePackageGrantee must not change" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, pre_launch.performancePackageTokenAmount, + "performancePackageTokenAmount must not change" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, pre_launch.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must not change" + ); + assert_eq!( + post_launch.teamAddress, pre_launch.teamAddress, + "teamAddress must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensClaimed, pre_launch.additionalTokensClaimed, + "additionalTokensClaimed must not change" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not change" + ); + assert_eq!( + post_launch.isPerformancePackageInitialized, pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/complete_launch.rs b/trident-tests/fuzz_launchpad/invariants/complete_launch.rs new file mode 100644 index 000000000..7276525cf --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/complete_launch.rs @@ -0,0 +1,227 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +// Keep in sync with `programs/v07_launchpad/src/lib.rs`. +const TOKEN_SCALE: u64 = 1_000_000; +const TOKENS_TO_PARTICIPANTS: u64 = 10_000_000 * TOKEN_SCALE; + +impl FuzzTest { + pub fn verify_complete_launch_invariants( + &mut self, + launch: Pubkey, + launch_authority: Pubkey, + dao: Pubkey, + squads_multisig_vault: Pubkey, + launch_quote_vault: Pubkey, + launch_base_vault: Pubkey, + treasury_quote_account: Pubkey, + bid_wall_quote_token_account: Pubkey, + pre_launch: &Launch, + pre_launch_quote_amount: u64, + pre_launch_base_amount: u64, + pre_treasury_quote_amount: u64, + pre_bid_wall_quote_amount: u64, + timestamp_before_tx: i64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after CompleteLaunch"); + + // Invariant 1: Validate requires Closed pre-state if tx succeeded. + assert!( + matches!(pre_launch.state, LaunchState::Closed), + "pre-state must be Closed if CompleteLaunch succeeded" + ); + + // Invariant 2: If within the 2-day authority window, the authority must match. + let timestamp_after_tx = self.trident.get_current_timestamp(); + if let Some(closed_at) = pre_launch.unixTimestampClosed { + let two_days_after_close = closed_at.saturating_add(60 * 60 * 24 * 2); + if two_days_after_close > timestamp_after_tx { + assert_eq!( + launch_authority, pre_launch.launchAuthority, + "launch_authority must match Launch.launchAuthority during authority-only window" + ); + } + } + + // Invariant 3: Early-return branch when totalApprovedAmount < minimumRaiseAmount. + if pre_launch.totalApprovedAmount < pre_launch.minimumRaiseAmount { + assert!( + matches!(post_launch.state, LaunchState::Refunding), + "post-state must be Refunding when approvals < minimum_raise_amount" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1" + ); + assert_eq!( + post_launch.dao, pre_launch.dao, + "dao must not be set in early-refund branch" + ); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not be set in early-refund branch" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not be set in early-refund branch" + ); + + let post_launch_quote_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault must exist") + .account + .amount; + let post_launch_base_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault must exist") + .account + .amount; + assert_eq!( + post_launch_quote_amount, pre_launch_quote_amount, + "launch_quote_vault must not change in early-refund branch" + ); + assert_eq!( + post_launch_base_amount, pre_launch_base_amount, + "launch_base_vault must not change in early-refund branch" + ); + return; + } + + // Invariant 4: Success branch when totalApprovedAmount >= minimumRaiseAmount. + assert!( + matches!(post_launch.state, LaunchState::Complete), + "post-state must be Complete when approvals >= minimum_raise_amount" + ); + assert_eq!( + post_launch.dao, + Some(dao), + "dao must be set to the dao account" + ); + assert_eq!( + post_launch.daoVault, + Some(squads_multisig_vault), + "daoVault must be set to squads_multisig_vault" + ); + let completed_at = post_launch + .unixTimestampCompleted + .expect("unixTimestampCompleted must be Some after CompleteLaunch success path"); + assert!( + completed_at >= timestamp_before_tx, + "unixTimestampCompleted must be >= timestamp_before_tx" + ); + assert!( + completed_at <= timestamp_after_tx, + "unixTimestampCompleted must be <= timestamp_after_tx" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on CompleteLaunch success path" + ); + + // Invariant 5: Launch totals must not change (only state/dao fields are updated here). + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + + // Invariant 6: Expected USDC allocations must be computed from pre-state. + let usdc_to_lp = pre_launch.totalApprovedAmount / 5; + let usdc_to_dao = pre_launch.totalApprovedAmount - usdc_to_lp; + let usdc_to_dao_treasury = usdc_to_dao.min(pre_launch.minimumRaiseAmount); + let usdc_to_bid_wall = usdc_to_dao - usdc_to_dao_treasury; + let refundable_usdc = pre_launch + .totalCommittedAmount + .checked_sub(pre_launch.totalApprovedAmount) + .expect( + "totalCommittedAmount must be >= totalApprovedAmount if CompleteLaunch succeeded", + ); + + // Invariant 7: Treasury quote account must increase by usdc_to_dao_treasury. + let post_treasury_quote_amount = self + .trident + .get_token_account(treasury_quote_account) + .expect("treasury_quote_account must exist after CompleteLaunch") + .account + .amount; + let treasury_delta = post_treasury_quote_amount + .checked_sub(pre_treasury_quote_amount) + .expect("treasury quote must not decrease"); + assert_eq!( + treasury_delta, usdc_to_dao_treasury, + "treasury_quote_account must increase by usdc_to_dao_treasury" + ); + + // Invariant 8: If a bid wall is funded, bid wall quote token account must increase by usdc_to_bid_wall. + if usdc_to_bid_wall > 0 { + let post_bid_wall_quote_amount = self + .trident + .get_token_account(bid_wall_quote_token_account) + .expect("bid_wall_quote_token_account must exist after CompleteLaunch") + .account + .amount; + let bid_wall_delta = post_bid_wall_quote_amount + .checked_sub(pre_bid_wall_quote_amount) + .expect("bid wall quote must not decrease"); + assert_eq!( + bid_wall_delta, usdc_to_bid_wall, + "bid_wall_quote_token_account must increase by usdc_to_bid_wall" + ); + } + + // Invariant 9: Launch quote vault must retain at least refundable_usdc. + let post_launch_quote_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault must exist after CompleteLaunch") + .account + .amount; + assert!( + post_launch_quote_amount >= refundable_usdc, + "launch_quote_vault must have at least refundable_usdc remaining" + ); + + // Invariant 10: Launch base vault must retain enough tokens for claim + additional + performance package. + let post_launch_base_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault must exist after CompleteLaunch") + .account + .amount; + let min_base_required = TOKENS_TO_PARTICIPANTS + .saturating_add(pre_launch.additionalTokensAmount) + .saturating_add(pre_launch.performancePackageTokenAmount); + assert!( + post_launch_base_amount >= min_base_required, + "launch_base_vault must retain enough tokens for claim + additional + performance package" + ); + assert!( + post_launch_quote_amount <= pre_launch_quote_amount, + "launch_quote_vault should not increase during CompleteLaunch" + ); + assert!( + post_launch_base_amount <= pre_launch_base_amount, + "launch_base_vault should not increase during CompleteLaunch" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/fund.rs b/trident-tests/fuzz_launchpad/invariants/fund.rs new file mode 100644 index 000000000..b98a7635c --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/fund.rs @@ -0,0 +1,285 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::FUNDING_RECORD_SEED_PREFIX; +use crate::common::types::launchpad_v_7::FundingRecord; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::common::types::launchpad_v_7::{self}; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_fund_invariants( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_signer: Pubkey, + launch_quote_vault: Pubkey, + funder: Pubkey, + funder_quote_account: Pubkey, + amount: u64, + pre_launch: &Launch, + pre_funding_record: Option<&FundingRecord>, + pre_launch_quote_vault_amount: u64, + pre_funder_quote_amount: u64, + timestamp_before_tx: i64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after Fund"); + let post_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .expect("FundingRecord must exist after Fund"); + + // Invariant 1: Basic success preconditions. + assert!(amount > 0, "amount must be > 0 if Fund succeeded"); + assert!( + matches!(pre_launch.state, LaunchState::Live), + "pre-state must be Live if Fund succeeded" + ); + + // Invariant 2: `launch` has_one constraints must hold. + assert_eq!( + pre_launch.launchSigner, launch_signer, + "launch_signer must match Launch.launchSigner" + ); + assert_eq!( + pre_launch.launchQuoteVault, launch_quote_vault, + "launch_quote_vault must match Launch.launchQuoteVault" + ); + + // Invariant 3: Launch state must stay Live. + assert!( + matches!(post_launch.state, LaunchState::Live), + "state must remain Live after Fund" + ); + + // Invariant 4: Launch must not be expired at time of tx. + let started = pre_launch + .unixTimestampStarted + .expect("unixTimestampStarted must be Some if Fund succeeded"); + let expiry = started.saturating_add(pre_launch.secondsForLaunch as i64); + let timestamp_after_tx = self.trident.get_current_timestamp(); + assert!( + timestamp_before_tx <= expiry, + "Fund must occur before expiry (timestamp_before_tx)" + ); + assert!( + timestamp_after_tx <= expiry, + "Fund must occur before expiry (timestamp_after_tx)" + ); + + // Invariant 5: FundingRecord PDA + bump must match expected derivation. + let (expected_fr_pda, expected_fr_bump) = self.trident.find_program_address( + &[FUNDING_RECORD_SEED_PREFIX, launch.as_ref(), funder.as_ref()], + &launchpad_v_7::program_id(), + ); + assert_eq!( + funding_record, expected_fr_pda, + "FundingRecord PDA must match seeds (funding_record, launch, funder)" + ); + assert_eq!( + post_funding_record.pdaBump, expected_fr_bump, + "FundingRecord.pdaBump must match PDA derivation bump" + ); + + // Invariant 6: FundingRecord fields must be correct and update rules must hold. + assert_eq!( + post_funding_record.funder, funder, + "FundingRecord.funder must equal funder" + ); + assert_eq!( + post_funding_record.launch, launch, + "FundingRecord.launch must equal launch" + ); + + match pre_funding_record { + Some(pre_fr) => { + // Existing record: committedAmount increases by amount. + assert_eq!( + post_funding_record.committedAmount, + pre_fr + .committedAmount + .checked_add(amount) + .expect("committedAmount overflow should be impossible"), + "committedAmount must increase by amount" + ); + // These flags should not flip during funding. + assert_eq!( + post_funding_record.isTokensClaimed, pre_fr.isTokensClaimed, + "isTokensClaimed must not change during Fund" + ); + assert_eq!( + post_funding_record.isUsdcRefunded, pre_fr.isUsdcRefunded, + "isUsdcRefunded must not change during Fund" + ); + assert_eq!( + post_funding_record.approvedAmount, pre_fr.approvedAmount, + "approvedAmount must not change during Fund" + ); + } + None => { + // New record: committedAmount == amount, flags false, approvedAmount == 0. + assert_eq!( + post_funding_record.committedAmount, amount, + "new FundingRecord.committedAmount must equal amount" + ); + assert!( + !post_funding_record.isTokensClaimed, + "new FundingRecord.isTokensClaimed must be false" + ); + assert!( + !post_funding_record.isUsdcRefunded, + "new FundingRecord.isUsdcRefunded must be false" + ); + assert_eq!( + post_funding_record.approvedAmount, 0, + "new FundingRecord.approvedAmount must start at 0" + ); + } + } + + // Invariant 7: Token accounting must match a transfer of `amount` from funder -> vault. + let post_vault_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault token account must exist after Fund") + .account + .amount; + let post_funder_amount = self + .trident + .get_token_account(funder_quote_account) + .expect("funder_quote_account token account must exist after Fund") + .account + .amount; + + let vault_delta = post_vault_amount + .checked_sub(pre_launch_quote_vault_amount) + .expect("vault amount must not decrease during Fund"); + let funder_delta = pre_funder_quote_amount + .checked_sub(post_funder_amount) + .expect("funder amount must not increase during Fund"); + + assert_eq!(vault_delta, amount, "vault must increase by amount"); + assert_eq!(funder_delta, amount, "funder must decrease by amount"); + + // Invariant 8: Launch accounting must reflect the funding. + assert_eq!( + post_launch.totalCommittedAmount, + pre_launch + .totalCommittedAmount + .checked_add(amount) + .expect("totalCommittedAmount overflow should be impossible"), + "Launch.totalCommittedAmount must increase by amount" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "Launch.seqNum must increment by exactly 1" + ); + + // Invariant 9: Unrelated Launch fields must not change. + assert_eq!( + post_launch.minimumRaiseAmount, pre_launch.minimumRaiseAmount, + "minimumRaiseAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, pre_launch.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, pre_launch.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must not change" + ); + assert_eq!( + post_launch.launchAuthority, pre_launch.launchAuthority, + "launchAuthority must not change" + ); + assert_eq!( + post_launch.launchSigner, pre_launch.launchSigner, + "launchSigner must not change" + ); + assert_eq!( + post_launch.launchSignerPdaBump, pre_launch.launchSignerPdaBump, + "launchSignerPdaBump must not change" + ); + assert_eq!( + post_launch.launchQuoteVault, pre_launch.launchQuoteVault, + "launchQuoteVault must not change" + ); + assert_eq!( + post_launch.launchBaseVault, pre_launch.launchBaseVault, + "launchBaseVault must not change" + ); + assert_eq!( + post_launch.baseMint, pre_launch.baseMint, + "baseMint must not change" + ); + assert_eq!( + post_launch.quoteMint, pre_launch.quoteMint, + "quoteMint must not change" + ); + assert_eq!( + post_launch.unixTimestampStarted, pre_launch.unixTimestampStarted, + "unixTimestampStarted must not change" + ); + assert_eq!( + post_launch.unixTimestampClosed, pre_launch.unixTimestampClosed, + "unixTimestampClosed must not change" + ); + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + assert_eq!( + post_launch.secondsForLaunch, pre_launch.secondsForLaunch, + "secondsForLaunch must not change" + ); + assert_eq!(post_launch.dao, pre_launch.dao, "dao must not change"); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not change" + ); + assert_eq!( + post_launch.performancePackageGrantee, pre_launch.performancePackageGrantee, + "performancePackageGrantee must not change" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, pre_launch.performancePackageTokenAmount, + "performancePackageTokenAmount must not change" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, pre_launch.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must not change" + ); + assert_eq!( + post_launch.teamAddress, pre_launch.teamAddress, + "teamAddress must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensClaimed, pre_launch.additionalTokensClaimed, + "additionalTokensClaimed must not change" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not change" + ); + assert_eq!( + post_launch.isPerformancePackageInitialized, pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/initialize_launch.rs b/trident-tests/fuzz_launchpad/invariants/initialize_launch.rs new file mode 100644 index 000000000..423d55123 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/initialize_launch.rs @@ -0,0 +1,235 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::LAUNCHPAD_SEED_PREFIX; +use crate::common::constants::LAUNCH_SIGNER_SEED_PREFIX; +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::types::launchpad_v_7::InitializeLaunchArgs; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::common::types::launchpad_v_7::{self}; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +const TOKEN_SCALE: u64 = 1_000_000; +const TOKENS_TO_PARTICIPANTS: u64 = 10_000_000 * TOKEN_SCALE; +const TOKENS_TO_FUTARCHY_LIQUIDITY: u64 = 2_000_000 * TOKEN_SCALE; +const TOKENS_TO_DAMM_V2_LIQUIDITY: u64 = 900_000 * TOKEN_SCALE; + +impl FuzzTest { + pub fn verify_initialize_launch_invariants( + &mut self, + launch: Pubkey, + base_mint: Pubkey, + quote_mint: Pubkey, + launch_signer: Pubkey, + quote_vault: Pubkey, + base_vault: Pubkey, + launch_authority: Pubkey, + additional_tokens_recipient: Pubkey, + args: &InitializeLaunchArgs, + pre_base_vault_amount: u64, + pre_quote_vault_amount: u64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after InitializeLaunch"); + + // Invariant 1: `launch` PDA and stored bump must match expected seeds. + let (expected_launch_pda, expected_launch_bump) = self.trident.find_program_address( + &[LAUNCHPAD_SEED_PREFIX, base_mint.as_ref()], + &launchpad_v_7::program_id(), + ); + assert_eq!(launch, expected_launch_pda, "Launch PDA must match seeds"); + assert_eq!( + post_launch.pdaBump, expected_launch_bump, + "Launch.pdaBump must match PDA derivation bump" + ); + + // Invariant 2: `launch_signer` PDA and stored bump must match expected seeds. + let (expected_signer_pda, expected_signer_bump) = self.trident.find_program_address( + &[LAUNCH_SIGNER_SEED_PREFIX, launch.as_ref()], + &launchpad_v_7::program_id(), + ); + assert_eq!( + launch_signer, expected_signer_pda, + "launch_signer PDA must match seeds" + ); + assert_eq!( + post_launch.launchSigner, launch_signer, + "Launch.launchSigner must equal the passed launch_signer PDA" + ); + assert_eq!( + post_launch.launchSignerPdaBump, expected_signer_bump, + "Launch.launchSignerPdaBump must match PDA derivation bump" + ); + + // Invariant 3: vaults must be ATAs for (owner=launch_signer, mint=quote/base). + let expected_quote_vault = self.trident.get_associated_token_address( + "e_mint, + &launch_signer, + &TOKEN_PROGRAM_ID, + ); + let expected_base_vault = self.trident.get_associated_token_address( + &base_mint, + &launch_signer, + &TOKEN_PROGRAM_ID, + ); + assert_eq!( + quote_vault, expected_quote_vault, + "quote_vault must be the ATA(quote_mint, launch_signer)" + ); + assert_eq!( + base_vault, expected_base_vault, + "base_vault must be the ATA(base_mint, launch_signer)" + ); + + // Invariant 4: persisted Launch fields must match args and fixed initialization values. + assert_eq!( + post_launch.minimumRaiseAmount, args.minimumRaiseAmount, + "minimumRaiseAmount must match args" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, args.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must match args" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, args.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must match args" + ); + assert_eq!( + post_launch.launchAuthority, launch_authority, + "launchAuthority must match passed launch_authority" + ); + assert_eq!( + post_launch.launchQuoteVault, quote_vault, + "launchQuoteVault must match passed quote_vault" + ); + assert_eq!( + post_launch.launchBaseVault, base_vault, + "launchBaseVault must match passed base_vault" + ); + assert_eq!( + post_launch.baseMint, base_mint, + "baseMint must match passed base_mint" + ); + assert_eq!( + post_launch.quoteMint, quote_mint, + "quoteMint must match passed quote_mint" + ); + assert_eq!( + post_launch.secondsForLaunch, args.secondsForLaunch, + "secondsForLaunch must match args" + ); + assert!( + matches!(post_launch.state, LaunchState::Initialized), + "Launch state must start as Initialized" + ); + assert_eq!(post_launch.seqNum, 0, "seqNum must start at 0"); + assert_eq!( + post_launch.totalCommittedAmount, 0, + "totalCommittedAmount must start at 0" + ); + assert_eq!( + post_launch.totalApprovedAmount, 0, + "totalApprovedAmount must start at 0" + ); + assert_eq!( + post_launch.unixTimestampStarted, None, + "unixTimestampStarted must start as None" + ); + assert_eq!( + post_launch.unixTimestampClosed, None, + "unixTimestampClosed must start as None" + ); + assert_eq!(post_launch.dao, None, "dao must start as None"); + assert_eq!(post_launch.daoVault, None, "daoVault must start as None"); + assert_eq!( + post_launch.performancePackageGrantee, args.performancePackageGrantee, + "performancePackageGrantee must match args" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, args.performancePackageTokenAmount, + "performancePackageTokenAmount must match args" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, args.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must match args" + ); + assert_eq!( + post_launch.teamAddress, args.teamAddress, + "teamAddress must match args" + ); + assert_eq!( + post_launch.additionalTokensAmount, args.additionalTokensAmount, + "additionalTokensAmount must match args" + ); + if args.additionalTokensAmount > 0 { + assert_eq!( + post_launch.additionalTokensRecipient, + Some(additional_tokens_recipient), + "additionalTokensRecipient must be Some(recipient) when additionalTokensAmount > 0" + ); + } else { + assert_eq!( + post_launch.additionalTokensRecipient, None, + "additionalTokensRecipient must be None when additionalTokensAmount == 0" + ); + } + assert!( + !post_launch.additionalTokensClaimed, + "additionalTokensClaimed must start false" + ); + assert_eq!( + post_launch.unixTimestampCompleted, None, + "unixTimestampCompleted must start as None" + ); + assert!( + !post_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must start false" + ); + + // Invariant 5: quote vault must not change during InitializeLaunch. + let post_quote_vault_amount = self + .trident + .get_token_account(quote_vault) + .expect("quote_vault token account must exist after InitializeLaunch") + .account + .amount; + assert_eq!( + post_quote_vault_amount, pre_quote_vault_amount, + "quote_vault amount must not change during InitializeLaunch" + ); + + // Invariant 6: base vault amount must increase by exactly the minted total. + let post_base_vault = self + .trident + .get_token_account(base_vault) + .expect("base_vault token account must exist after InitializeLaunch") + .account; + assert_eq!( + post_base_vault.mint, base_mint, + "base_vault mint must equal base_mint" + ); + assert_eq!( + post_base_vault.owner, launch_signer, + "base_vault owner must equal launch_signer" + ); + + let expected_mint_to = args + .performancePackageTokenAmount + .saturating_add(args.additionalTokensAmount) + .saturating_add(TOKENS_TO_PARTICIPANTS) + .saturating_add(TOKENS_TO_FUTARCHY_LIQUIDITY) + .saturating_add(TOKENS_TO_DAMM_V2_LIQUIDITY); + + let base_delta = post_base_vault + .amount + .checked_sub(pre_base_vault_amount) + .expect("base_vault amount must not decrease during InitializeLaunch"); + assert_eq!( + base_delta, expected_mint_to, + "base_vault must be minted exactly the expected total supply" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/initialize_performance_package.rs b/trident-tests/fuzz_launchpad/invariants/initialize_performance_package.rs new file mode 100644 index 000000000..311d8393f --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/initialize_performance_package.rs @@ -0,0 +1,225 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::PERFORMANCE_PACKAGE_SEED_PREFIX; +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::PerformancePackageState; +use crate::common::types::price_based_performance_package::{self}; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_initialize_performance_package_invariants( + &mut self, + launch: Pubkey, + launch_signer: Pubkey, + launch_base_vault: Pubkey, + base_mint: Pubkey, + dao: Pubkey, + squads_multisig_vault: Pubkey, + performance_package: Pubkey, + performance_package_token_account: Pubkey, + pre_launch: &Launch, + pre_launch_base_vault_amount: u64, + pre_pp_vault_amount: u64, + ) { + const TOKENS_TO_PARTICIPANTS: u64 = 10_000_000 * 1_000_000; + const PRICE_SCALE: u128 = 1_000_000_000_000; + const THREE_MONTHS_SECONDS: u32 = 3 * 30 * 24 * 60 * 60; + + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after InitializePerformancePackage"); + + // Invariant 1: Precondition checks. + assert!( + matches!(pre_launch.state, LaunchState::Complete), + "pre-state must be Complete if InitializePerformancePackage succeeded" + ); + assert!( + !pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must be false if InitializePerformancePackage succeeded" + ); + assert_eq!( + pre_launch.dao, + Some(dao), + "launch.dao must equal dao if tx succeeded" + ); + assert_eq!( + pre_launch.launchSigner, launch_signer, + "launch_signer must match Launch.launchSigner" + ); + assert_eq!( + pre_launch.launchBaseVault, launch_base_vault, + "launch_base_vault must match Launch.launchBaseVault" + ); + assert_eq!( + pre_launch.baseMint, base_mint, + "base_mint must match Launch.baseMint" + ); + + // Invariant 2: Launch mutations must be correct. + assert!( + post_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must flip true" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "Launch.seqNum must increment by exactly 1" + ); + + // Invariant 3: PBPP PerformancePackage must exist and match PDA derivation. + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after InitializePerformancePackage"); + + let (expected_pp_pda, expected_pp_bump) = self.trident.find_program_address( + &[PERFORMANCE_PACKAGE_SEED_PREFIX, launch_signer.as_ref()], + &price_based_performance_package::program_id(), + ); + assert_eq!( + performance_package, expected_pp_pda, + "PerformancePackage PDA must match seeds (performance_package, launch_signer)" + ); + assert_eq!( + post_pp.createKey, launch_signer, + "PerformancePackage.createKey must equal launch_signer" + ); + assert_eq!( + post_pp.pdaBump, expected_pp_bump, + "PerformancePackage.pdaBump must match PDA bump" + ); + + // Invariant 4: PerformancePackage static fields must match expected values. + assert_eq!( + post_pp.tokenMint, base_mint, + "PerformancePackage.tokenMint must equal base_mint" + ); + assert_eq!( + post_pp.recipient, pre_launch.performancePackageGrantee, + "PerformancePackage.recipient must equal performance_package_grantee" + ); + assert_eq!( + post_pp.performancePackageAuthority, squads_multisig_vault, + "PerformancePackage.performancePackageAuthority must equal squads_multisig_vault" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, dao, + "PerformancePackage.oracleAccount must be dao" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, 9, + "PerformancePackage.oracle byteOffset must be 8 + 1" + ); + assert_eq!( + post_pp.twapLengthSeconds, THREE_MONTHS_SECONDS, + "twapLengthSeconds must be 3 months" + ); + assert!( + matches!(post_pp.state, PerformancePackageState::Locked), + "PerformancePackage must start Locked" + ); + assert_eq!( + post_pp.seqNum, 0, + "PerformancePackage.seqNum must start at 0" + ); + assert_eq!( + post_pp.alreadyUnlockedAmount, 0, + "alreadyUnlockedAmount must start at 0" + ); + + // Invariant 5: Tranches must match expected structure (5 tranches, thresholds 2x..32x). + assert_eq!(post_pp.tranches.len(), 5, "must have exactly 5 tranches"); + let tranche_amount = pre_launch.performancePackageTokenAmount / 5; + let expected_total = tranche_amount * 5; + let launch_price_1e12: u128 = ((pre_launch.totalApprovedAmount as u128) * PRICE_SCALE) + / (TOKENS_TO_PARTICIPANTS as u128); + + let expected_thresholds = [ + launch_price_1e12 * 2, + launch_price_1e12 * 4, + launch_price_1e12 * 8, + launch_price_1e12 * 16, + launch_price_1e12 * 32, + ]; + + for (i, tranche) in post_pp.tranches.iter().enumerate() { + assert_eq!( + tranche.tokenAmount, tranche_amount, + "tranche[{i}].tokenAmount must be performancePackageTokenAmount/5" + ); + assert_eq!( + tranche.priceThreshold, expected_thresholds[i], + "tranche[{i}].priceThreshold must match expected multiple" + ); + assert!(!tranche.isUnlocked, "tranche[{i}] must start locked"); + } + assert_eq!( + post_pp.totalTokenAmount, expected_total, + "totalTokenAmount must equal sum(tranche token amounts)" + ); + + // Invariant 6: minUnlockTimestamp must match completed + months*30d. + let completed = pre_launch.unixTimestampCompleted.expect( + "unixTimestampCompleted must be Some if InitializePerformancePackage succeeded", + ); + let expected_min_unlock = completed + .saturating_add(pre_launch.monthsUntilInsidersCanUnlock as i64 * 30 * 24 * 60 * 60); + assert_eq!( + post_pp.minUnlockTimestamp, expected_min_unlock, + "minUnlockTimestamp must match expected" + ); + + // Invariant 7: PBPP token vault must be ATA(base_mint, performance_package) and be stored correctly. + let expected_pp_vault = self.trident.get_associated_token_address( + &base_mint, + &performance_package, + &TOKEN_PROGRAM_ID, + ); + assert_eq!( + performance_package_token_account, expected_pp_vault, + "performance_package_token_account must be ATA(base_mint, performance_package)" + ); + assert_eq!( + post_pp.performancePackageTokenVault, performance_package_token_account, + "stored PerformancePackageTokenVault must match passed account" + ); + + // Invariant 8: Token accounting must reflect a transfer of expected_total from launch_base_vault -> pp_vault. + let post_launch_base_vault_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault must exist after InitializePerformancePackage") + .account + .amount; + let post_pp_vault_amount = self + .trident + .get_token_account(performance_package_token_account) + .expect("pp vault must exist after InitializePerformancePackage") + .account + .amount; + + let base_vault_delta = pre_launch_base_vault_amount + .checked_sub(post_launch_base_vault_amount) + .expect("launch_base_vault must not increase"); + let pp_vault_delta = post_pp_vault_amount + .checked_sub(pre_pp_vault_amount) + .expect("pp vault must not decrease"); + assert_eq!( + base_vault_delta, expected_total, + "launch_base_vault must decrease by expected_total" + ); + assert_eq!( + pp_vault_delta, expected_total, + "pp vault must increase by expected_total" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/mod.rs b/trident-tests/fuzz_launchpad/invariants/mod.rs new file mode 100644 index 000000000..3612691e5 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/mod.rs @@ -0,0 +1,10 @@ +pub mod claim; +pub mod claim_additional_token_allocation; +pub mod close_launch; +pub mod complete_launch; +pub mod fund; +pub mod initialize_launch; +pub mod initialize_performance_package; +pub mod refund; +pub mod set_funding_record_approval; +pub mod start_launch; diff --git a/trident-tests/fuzz_launchpad/invariants/refund.rs b/trident-tests/fuzz_launchpad/invariants/refund.rs new file mode 100644 index 000000000..a4d20703f --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/refund.rs @@ -0,0 +1,257 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::FUNDING_RECORD_SEED_PREFIX; +use crate::common::types::launchpad_v_7::FundingRecord; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::common::types::launchpad_v_7::{self}; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_refund_invariants( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_quote_vault: Pubkey, + launch_signer: Pubkey, + funder: Pubkey, + funder_quote_account: Pubkey, + pre_launch: &Launch, + pre_funding_record: &FundingRecord, + pre_vault_amount: u64, + pre_funder_amount: u64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after Refund"); + let post_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .expect("FundingRecord must exist after Refund"); + + // Invariant 1: `launch` has_one constraints must hold. + assert_eq!( + pre_launch.launchQuoteVault, launch_quote_vault, + "launch_quote_vault must match Launch.launchQuoteVault" + ); + assert_eq!( + pre_launch.launchSigner, launch_signer, + "launch_signer must match Launch.launchSigner" + ); + + // Invariant 2: FundingRecord must match seeds and funder constraints. + let (expected_fr_pda, _) = self.trident.find_program_address( + &[FUNDING_RECORD_SEED_PREFIX, launch.as_ref(), funder.as_ref()], + &launchpad_v_7::program_id(), + ); + assert_eq!( + funding_record, expected_fr_pda, + "FundingRecord PDA must match seeds (funding_record, launch, funder)" + ); + assert_eq!( + pre_funding_record.funder, funder, + "FundingRecord.funder must match provided funder" + ); + assert_eq!( + pre_funding_record.launch, launch, + "FundingRecord.launch must equal launch" + ); + + // Invariant 3: Refund is only valid in (Refunding|Complete) and only once. + assert!( + matches!(pre_launch.state, LaunchState::Refunding) + || matches!(pre_launch.state, LaunchState::Complete), + "pre-state must be Refunding or Complete if Refund succeeded" + ); + assert!( + !pre_funding_record.isUsdcRefunded, + "pre FundingRecord.isUsdcRefunded must be false if Refund succeeded" + ); + + // Invariant 4: Refund must not change launch state. + match (&post_launch.state, &pre_launch.state) { + (LaunchState::Refunding, LaunchState::Refunding) + | (LaunchState::Complete, LaunchState::Complete) => {} + _ => panic!("Launch.state must not change during Refund"), + } + + // Invariant 5: Amount refunded depends on launch state. + let amount_to_refund = match pre_launch.state { + LaunchState::Refunding => pre_funding_record.committedAmount, + LaunchState::Complete => pre_funding_record + .committedAmount + .checked_sub(pre_funding_record.approvedAmount) + .expect("committedAmount must be >= approvedAmount"), + _ => unreachable!(), + }; + + // Invariant 6: FundingRecord mutation must flip isUsdcRefunded and preserve other fields. + assert!( + post_funding_record.isUsdcRefunded, + "FundingRecord.isUsdcRefunded must be true after Refund" + ); + assert_eq!( + post_funding_record.committedAmount, pre_funding_record.committedAmount, + "committedAmount must not change" + ); + assert_eq!( + post_funding_record.approvedAmount, pre_funding_record.approvedAmount, + "approvedAmount must not change" + ); + assert_eq!( + post_funding_record.isTokensClaimed, pre_funding_record.isTokensClaimed, + "isTokensClaimed must not change" + ); + assert_eq!( + post_funding_record.funder, pre_funding_record.funder, + "funder must not change" + ); + assert_eq!( + post_funding_record.launch, pre_funding_record.launch, + "launch must not change" + ); + + // Invariant 7: Token accounting must match amount_to_refund (vault -> funder). + let post_vault_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault token account must exist after Refund") + .account + .amount; + let post_funder_amount = self + .trident + .get_token_account(funder_quote_account) + .expect("funder_quote_account token account must exist after Refund") + .account + .amount; + + let vault_delta = pre_vault_amount + .checked_sub(post_vault_amount) + .expect("vault amount must not increase during Refund"); + let funder_delta = post_funder_amount + .checked_sub(pre_funder_amount) + .expect("funder amount must not decrease during Refund"); + + assert_eq!( + vault_delta, amount_to_refund, + "vault must decrease by amount_to_refund" + ); + assert_eq!( + funder_delta, amount_to_refund, + "funder must increase by amount_to_refund" + ); + + // Invariant 8: Launch seqNum must increment by 1; other fields must not change. + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on Refund" + ); + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + assert_eq!( + post_launch.unixTimestampStarted, pre_launch.unixTimestampStarted, + "unixTimestampStarted must not change" + ); + assert_eq!( + post_launch.unixTimestampClosed, pre_launch.unixTimestampClosed, + "unixTimestampClosed must not change" + ); + assert_eq!( + post_launch.minimumRaiseAmount, pre_launch.minimumRaiseAmount, + "minimumRaiseAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, pre_launch.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, pre_launch.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must not change" + ); + assert_eq!( + post_launch.launchAuthority, pre_launch.launchAuthority, + "launchAuthority must not change" + ); + assert_eq!( + post_launch.launchSigner, pre_launch.launchSigner, + "launchSigner must not change" + ); + assert_eq!( + post_launch.launchSignerPdaBump, pre_launch.launchSignerPdaBump, + "launchSignerPdaBump must not change" + ); + assert_eq!( + post_launch.launchQuoteVault, pre_launch.launchQuoteVault, + "launchQuoteVault must not change" + ); + assert_eq!( + post_launch.launchBaseVault, pre_launch.launchBaseVault, + "launchBaseVault must not change" + ); + assert_eq!( + post_launch.baseMint, pre_launch.baseMint, + "baseMint must not change" + ); + assert_eq!( + post_launch.quoteMint, pre_launch.quoteMint, + "quoteMint must not change" + ); + assert_eq!( + post_launch.secondsForLaunch, pre_launch.secondsForLaunch, + "secondsForLaunch must not change" + ); + assert_eq!(post_launch.dao, pre_launch.dao, "dao must not change"); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not change" + ); + assert_eq!( + post_launch.performancePackageGrantee, pre_launch.performancePackageGrantee, + "performancePackageGrantee must not change" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, pre_launch.performancePackageTokenAmount, + "performancePackageTokenAmount must not change" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, pre_launch.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must not change" + ); + assert_eq!( + post_launch.teamAddress, pre_launch.teamAddress, + "teamAddress must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensClaimed, pre_launch.additionalTokensClaimed, + "additionalTokensClaimed must not change" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not change" + ); + assert_eq!( + post_launch.isPerformancePackageInitialized, pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/set_funding_record_approval.rs b/trident-tests/fuzz_launchpad/invariants/set_funding_record_approval.rs new file mode 100644 index 000000000..6f67c0b3f --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/set_funding_record_approval.rs @@ -0,0 +1,213 @@ +use crate::common::types::launchpad_v_7::FundingRecord; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_set_funding_record_approval_invariants( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_authority: Pubkey, + approved_amount: u64, + pre_launch: &Launch, + pre_funding_record: &FundingRecord, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after SetFundingRecordApproval"); + let post_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .expect("FundingRecord account must exist after SetFundingRecordApproval"); + + // Invariant 1: Authority must match (has_one). + assert_eq!( + launch_authority, pre_launch.launchAuthority, + "launch_authority must match Launch.launchAuthority" + ); + + // Invariant 2: Must be in Closed state if tx succeeded. + assert!( + matches!(pre_launch.state, LaunchState::Closed), + "pre-state must be Closed if SetFundingRecordApproval succeeded" + ); + assert!( + matches!(post_launch.state, LaunchState::Closed), + "state must remain Closed after SetFundingRecordApproval" + ); + + // Invariant 3: Close timestamp must exist and approval window must not be over. + let closed_at = pre_launch + .unixTimestampClosed + .expect("unixTimestampClosed must be Some if SetFundingRecordApproval succeeded"); + let now = self.trident.get_current_timestamp(); + let two_days_after_close = closed_at.saturating_add(60 * 60 * 24 * 2); + assert!( + now <= two_days_after_close, + "approval must occur within 2 days of unixTimestampClosed" + ); + + // Invariant 4: FundingRecord must belong to launch and approved_amount <= committedAmount. + assert_eq!( + pre_funding_record.launch, launch, + "FundingRecord.launch must equal launch" + ); + assert_eq!( + post_funding_record.launch, launch, + "FundingRecord.launch must remain launch" + ); + assert!( + approved_amount <= pre_funding_record.committedAmount, + "approved_amount must be <= committedAmount if tx succeeded" + ); + + // Invariant 5: FundingRecord mutation must set approvedAmount and preserve other fields. + assert_eq!( + post_funding_record.approvedAmount, approved_amount, + "FundingRecord.approvedAmount must equal the instruction input" + ); + assert_eq!( + post_funding_record.committedAmount, pre_funding_record.committedAmount, + "FundingRecord.committedAmount must not change" + ); + assert_eq!( + post_funding_record.funder, pre_funding_record.funder, + "FundingRecord.funder must not change" + ); + assert_eq!( + post_funding_record.isTokensClaimed, pre_funding_record.isTokensClaimed, + "FundingRecord.isTokensClaimed must not change" + ); + assert_eq!( + post_funding_record.isUsdcRefunded, pre_funding_record.isUsdcRefunded, + "FundingRecord.isUsdcRefunded must not change" + ); + + // Invariant 6: Launch mutation must adjust totalApprovedAmount by delta and increment seqNum by 1. + let expected_total_approved = if approved_amount >= pre_funding_record.approvedAmount { + pre_launch + .totalApprovedAmount + .checked_add(approved_amount - pre_funding_record.approvedAmount) + .expect("totalApprovedAmount overflow should be impossible") + } else { + pre_launch + .totalApprovedAmount + .checked_sub(pre_funding_record.approvedAmount - approved_amount) + .expect("totalApprovedAmount underflow should be impossible") + }; + assert_eq!( + post_launch.totalApprovedAmount, expected_total_approved, + "Launch.totalApprovedAmount must change by exactly the delta of approvedAmount" + ); + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "Launch.seqNum must increment by exactly 1" + ); + + // Invariant 7: Unrelated launch fields must not change. + assert_eq!( + post_launch.minimumRaiseAmount, pre_launch.minimumRaiseAmount, + "minimumRaiseAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, pre_launch.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, pre_launch.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must not change" + ); + assert_eq!( + post_launch.launchAuthority, pre_launch.launchAuthority, + "launchAuthority must not change" + ); + assert_eq!( + post_launch.launchSigner, pre_launch.launchSigner, + "launchSigner must not change" + ); + assert_eq!( + post_launch.launchSignerPdaBump, pre_launch.launchSignerPdaBump, + "launchSignerPdaBump must not change" + ); + assert_eq!( + post_launch.launchQuoteVault, pre_launch.launchQuoteVault, + "launchQuoteVault must not change" + ); + assert_eq!( + post_launch.launchBaseVault, pre_launch.launchBaseVault, + "launchBaseVault must not change" + ); + assert_eq!( + post_launch.baseMint, pre_launch.baseMint, + "baseMint must not change" + ); + assert_eq!( + post_launch.quoteMint, pre_launch.quoteMint, + "quoteMint must not change" + ); + assert_eq!( + post_launch.unixTimestampStarted, pre_launch.unixTimestampStarted, + "unixTimestampStarted must not change" + ); + assert_eq!( + post_launch.unixTimestampClosed, pre_launch.unixTimestampClosed, + "unixTimestampClosed must not change" + ); + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + assert_eq!( + post_launch.secondsForLaunch, pre_launch.secondsForLaunch, + "secondsForLaunch must not change" + ); + assert_eq!(post_launch.dao, pre_launch.dao, "dao must not change"); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not change" + ); + assert_eq!( + post_launch.performancePackageGrantee, pre_launch.performancePackageGrantee, + "performancePackageGrantee must not change" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, pre_launch.performancePackageTokenAmount, + "performancePackageTokenAmount must not change" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, pre_launch.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must not change" + ); + assert_eq!( + post_launch.teamAddress, pre_launch.teamAddress, + "teamAddress must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensClaimed, pre_launch.additionalTokensClaimed, + "additionalTokensClaimed must not change" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not change" + ); + assert_eq!( + post_launch.isPerformancePackageInitialized, pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/invariants/start_launch.rs b/trident-tests/fuzz_launchpad/invariants/start_launch.rs new file mode 100644 index 000000000..dade703e9 --- /dev/null +++ b/trident-tests/fuzz_launchpad/invariants/start_launch.rs @@ -0,0 +1,158 @@ +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_start_launch_invariants( + &mut self, + launch: Pubkey, + launch_authority: Pubkey, + pre_launch: &Launch, + timestamp_before_tx: i64, + ) { + let post_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist after StartLaunch"); + + // Invariant 1: Authority must match (has_one). + assert_eq!( + launch_authority, pre_launch.launchAuthority, + "launch_authority must match Launch.launchAuthority" + ); + + // Invariant 2: State transition must be Initialized -> Live. + assert!( + matches!(pre_launch.state, LaunchState::Initialized), + "pre-state must be Initialized if StartLaunch succeeded" + ); + assert!( + matches!(post_launch.state, LaunchState::Live), + "post-state must be Live after StartLaunch" + ); + + // Invariant 3: unixTimestampStarted must be set within [timestamp_before_tx, timestamp_after_tx]. + let timestamp_after_tx = self.trident.get_current_timestamp(); + let started = post_launch + .unixTimestampStarted + .expect("unixTimestampStarted must be Some after StartLaunch"); + assert!( + started >= timestamp_before_tx, + "unixTimestampStarted must be >= timestamp_before_tx" + ); + assert!( + started <= timestamp_after_tx, + "unixTimestampStarted must be <= timestamp_after_tx" + ); + + // Invariant 4: seqNum must increment by exactly 1. + assert_eq!( + post_launch.seqNum, + pre_launch + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on StartLaunch" + ); + + // Invariant 5: Unrelated fields must not change. + assert_eq!( + post_launch.minimumRaiseAmount, pre_launch.minimumRaiseAmount, + "minimumRaiseAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitAmount, pre_launch.monthlySpendingLimitAmount, + "monthlySpendingLimitAmount must not change" + ); + assert_eq!( + post_launch.monthlySpendingLimitMembers, pre_launch.monthlySpendingLimitMembers, + "monthlySpendingLimitMembers must not change" + ); + assert_eq!( + post_launch.launchAuthority, pre_launch.launchAuthority, + "launchAuthority must not change" + ); + assert_eq!( + post_launch.launchSigner, pre_launch.launchSigner, + "launchSigner must not change" + ); + assert_eq!( + post_launch.launchSignerPdaBump, pre_launch.launchSignerPdaBump, + "launchSignerPdaBump must not change" + ); + assert_eq!( + post_launch.launchQuoteVault, pre_launch.launchQuoteVault, + "launchQuoteVault must not change" + ); + assert_eq!( + post_launch.launchBaseVault, pre_launch.launchBaseVault, + "launchBaseVault must not change" + ); + assert_eq!( + post_launch.baseMint, pre_launch.baseMint, + "baseMint must not change" + ); + assert_eq!( + post_launch.quoteMint, pre_launch.quoteMint, + "quoteMint must not change" + ); + assert_eq!( + post_launch.unixTimestampClosed, pre_launch.unixTimestampClosed, + "unixTimestampClosed must not change" + ); + assert_eq!( + post_launch.totalCommittedAmount, pre_launch.totalCommittedAmount, + "totalCommittedAmount must not change" + ); + assert_eq!( + post_launch.totalApprovedAmount, pre_launch.totalApprovedAmount, + "totalApprovedAmount must not change" + ); + assert_eq!( + post_launch.secondsForLaunch, pre_launch.secondsForLaunch, + "secondsForLaunch must not change" + ); + assert_eq!(post_launch.dao, pre_launch.dao, "dao must not change"); + assert_eq!( + post_launch.daoVault, pre_launch.daoVault, + "daoVault must not change" + ); + assert_eq!( + post_launch.performancePackageGrantee, pre_launch.performancePackageGrantee, + "performancePackageGrantee must not change" + ); + assert_eq!( + post_launch.performancePackageTokenAmount, pre_launch.performancePackageTokenAmount, + "performancePackageTokenAmount must not change" + ); + assert_eq!( + post_launch.monthsUntilInsidersCanUnlock, pre_launch.monthsUntilInsidersCanUnlock, + "monthsUntilInsidersCanUnlock must not change" + ); + assert_eq!( + post_launch.teamAddress, pre_launch.teamAddress, + "teamAddress must not change" + ); + assert_eq!( + post_launch.additionalTokensAmount, pre_launch.additionalTokensAmount, + "additionalTokensAmount must not change" + ); + assert_eq!( + post_launch.additionalTokensRecipient, pre_launch.additionalTokensRecipient, + "additionalTokensRecipient must not change" + ); + assert_eq!( + post_launch.additionalTokensClaimed, pre_launch.additionalTokensClaimed, + "additionalTokensClaimed must not change" + ); + assert_eq!( + post_launch.unixTimestampCompleted, pre_launch.unixTimestampCompleted, + "unixTimestampCompleted must not change" + ); + assert_eq!( + post_launch.isPerformancePackageInitialized, pre_launch.isPerformancePackageInitialized, + "isPerformancePackageInitialized must not change" + ); + } +} diff --git a/trident-tests/fuzz_launchpad/methods/launchpad.rs b/trident-tests/fuzz_launchpad/methods/launchpad.rs new file mode 100644 index 000000000..0fbbbed60 --- /dev/null +++ b/trident-tests/fuzz_launchpad/methods/launchpad.rs @@ -0,0 +1,799 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::types::launchpad_v_7::MeteoraAccountsInstructionAccounts; +use crate::common::types::launchpad_v_7::StaticAccountsInstructionAccounts; +use crate::common::types::launchpad_v_7::*; +use crate::common::types::*; +use crate::FuzzTest; +use solana_sdk::compute_budget::ComputeBudgetInstruction; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::pda::get_amm_position_pda; +use crate::common::pda::get_bid_wall_pda; +use crate::common::pda::get_dao_pda; +use crate::common::pda::get_event_authority_pda; +use crate::common::pda::get_pool_authority_pda; +use crate::common::pda::get_pool_creator_authority_pda; +use crate::common::pda::get_pool_pda; +use crate::common::pda::get_position_nft_account_pda; +use crate::common::pda::get_position_nft_mint_pda; +use crate::common::pda::get_position_pda; +use crate::common::pda::get_squads_multisig_pda; +use crate::common::pda::get_squads_multisig_spending_limit_pda; +use crate::common::pda::get_squads_multisig_vault_pda; +use crate::common::pda::get_token_a_vault_pda; +use crate::common::pda::get_token_b_vault_pda; +use crate::common::token::get_or_initialize_associated_token_account; + +impl FuzzTest { + pub fn initialize_launch( + &mut self, + payer: Pubkey, + launch: Pubkey, + base_mint: Pubkey, + quote_mint: Pubkey, + token_metadata: Pubkey, + launch_signer: Pubkey, + quote_vault: Pubkey, + base_vault: Pubkey, + launch_authority: Pubkey, + additional_tokens_recipient: Pubkey, + args: InitializeLaunchArgs, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let args_for_invariants = args.clone(); + let pre_quote_vault_amount = self + .trident + .get_token_account(quote_vault) + .map(|acc| acc.account.amount) + .unwrap_or(0); + let pre_base_vault_amount = self + .trident + .get_token_account(base_vault) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + let init_launch = launchpad_v_7::InitializeLaunchInstruction::data( + launchpad_v_7::InitializeLaunchInstructionData::new(args), + ) + .accounts(launchpad_v_7::InitializeLaunchInstructionAccounts::new( + launch, + base_mint, + token_metadata, + launch_signer, + quote_vault, + base_vault, + payer, + launch_authority, + quote_mint, + additional_tokens_recipient, + RENT_SYSVAR_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + SOLANA_PROGRAM_ID, + MPL_TOKEN_METADATA_PROGRAM_ID, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[init_launch], message); + + // Verify invariants + if !res.is_success() { + return; + } + + self.verify_initialize_launch_invariants( + launch, + base_mint, + quote_mint, + launch_signer, + quote_vault, + base_vault, + launch_authority, + additional_tokens_recipient, + &args_for_invariants, + pre_base_vault_amount, + pre_quote_vault_amount, + ); + } + + pub fn fund( + &mut self, + payer: Pubkey, + launch: Pubkey, + funding_record: Pubkey, + launch_signer: Pubkey, + launch_quote_vault: Pubkey, + funder: Pubkey, + funder_quote_account: Pubkey, + amount: u64, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before Fund"); + let pre_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)); + let pre_launch_quote_vault_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault token account must exist before Fund") + .account + .amount; + let pre_funder_quote_amount = self + .trident + .get_token_account(funder_quote_account) + .expect("funder_quote_account token account must exist before Fund") + .account + .amount; + + let fund_launch = + launchpad_v_7::FundInstruction::data(launchpad_v_7::FundInstructionData::new(amount)) + .accounts(launchpad_v_7::FundInstructionAccounts::new( + launch, + funding_record, + launch_signer, + launch_quote_vault, + funder, + payer, + funder_quote_account, + TOKEN_PROGRAM_ID, + SOLANA_PROGRAM_ID, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[fund_launch], message); + if !res.is_success() { + return; + } + // Verify invariants + self.verify_fund_invariants( + launch, + funding_record, + launch_signer, + launch_quote_vault, + funder, + funder_quote_account, + amount, + &pre_launch, + pre_funding_record.as_ref(), + pre_launch_quote_vault_amount, + pre_funder_quote_amount, + timestamp_before_tx, + ); + } + + pub fn start_launch( + &mut self, + launch: Pubkey, + launch_authority: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before StartLaunch"); + + let start_launch = launchpad_v_7::StartLaunchInstruction::data( + launchpad_v_7::StartLaunchInstructionData::new(), + ) + .accounts(launchpad_v_7::StartLaunchInstructionAccounts::new( + launch, + launch_authority, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[start_launch], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_start_launch_invariants( + launch, + launch_authority, + &pre_launch, + timestamp_before_tx, + ); + } + + pub fn set_funding_record_approval( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_authority: Pubkey, + approved_amount: u64, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before SetFundingRecordApproval"); + let pre_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .expect("FundingRecord must exist before SetFundingRecordApproval"); + + let set_funding_record_approval = launchpad_v_7::SetFundingRecordApprovalInstruction::data( + launchpad_v_7::SetFundingRecordApprovalInstructionData::new(approved_amount), + ) + .accounts( + launchpad_v_7::SetFundingRecordApprovalInstructionAccounts::new( + launch, + funding_record, + launch_authority, + event_authority, + launchpad_v_7::program_id(), + ), + ) + .instruction(); + + let res = self + .trident + .process_transaction(&[set_funding_record_approval], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_set_funding_record_approval_invariants( + launch, + funding_record, + launch_authority, + approved_amount, + &pre_launch, + &pre_funding_record, + ); + } + + pub fn close_launch(&mut self, launch: Pubkey, message: Option<&str>) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before CloseLaunch"); + + let close_launch = launchpad_v_7::CloseLaunchInstruction::data( + launchpad_v_7::CloseLaunchInstructionData::new(), + ) + .accounts(launchpad_v_7::CloseLaunchInstructionAccounts::new( + launch, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[close_launch], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_close_launch_invariants(launch, &pre_launch, timestamp_before_tx); + } + + pub fn refund( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_quote_vault: Pubkey, + launch_signer: Pubkey, + funder: Pubkey, + funder_quote_account: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before Refund"); + let pre_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)); + if pre_funding_record.is_none() { + return; + } + let pre_funding_record = pre_funding_record.unwrap(); + let pre_vault_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault token account must exist before Refund") + .account + .amount; + let pre_funder_amount = self + .trident + .get_token_account(funder_quote_account) + .expect("funder_quote_account token account must exist before Refund") + .account + .amount; + + let refund = + launchpad_v_7::RefundInstruction::data(launchpad_v_7::RefundInstructionData::new()) + .accounts(launchpad_v_7::RefundInstructionAccounts::new( + launch, + funding_record, + launch_quote_vault, + launch_signer, + funder, + funder_quote_account, + TOKEN_PROGRAM_ID, + SOLANA_PROGRAM_ID, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[refund], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_refund_invariants( + launch, + funding_record, + launch_quote_vault, + launch_signer, + funder, + funder_quote_account, + &pre_launch, + &pre_funding_record, + pre_vault_amount, + pre_funder_amount, + ); + } + + pub fn claim( + &mut self, + launch: Pubkey, + funding_record: Pubkey, + launch_signer: Pubkey, + base_mint: Pubkey, + launch_base_vault: Pubkey, + funder: Pubkey, + funder_quote_account: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before Claim"); + let pre_funding_record = self + .trident + .get_account_with_type::(&funding_record, Some(8)); + if pre_funding_record.is_none() { + return; + } + let pre_funding_record = pre_funding_record.unwrap(); + let pre_vault_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault token account must exist before Claim") + .account + .amount; + let pre_funder_token_amount = self + .trident + .get_token_account(funder_quote_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + let claim = + launchpad_v_7::ClaimInstruction::data(launchpad_v_7::ClaimInstructionData::new()) + .accounts(launchpad_v_7::ClaimInstructionAccounts::new( + launch, + funding_record, + launch_signer, + base_mint, + launch_base_vault, + funder, + funder_quote_account, + TOKEN_PROGRAM_ID, + SOLANA_PROGRAM_ID, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[claim], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_claim_invariants( + launch, + funding_record, + launch_signer, + base_mint, + launch_base_vault, + funder, + funder_quote_account, + &pre_launch, + &pre_funding_record, + pre_vault_amount, + pre_funder_token_amount, + ); + } + + pub fn claim_additional_token_allocation( + &mut self, + launch: Pubkey, + payer: Pubkey, + launch_signer: Pubkey, + base_vault: Pubkey, + base_mint: Pubkey, + additional_tokens_recipient: Pubkey, + additional_tokens_recipient_token_account: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before ClaimAdditionalTokenAllocation"); + let pre_base_vault_amount = self + .trident + .get_token_account(base_vault) + .expect("base_vault token account must exist before ClaimAdditionalTokenAllocation") + .account + .amount; + let pre_recipient_amount = self + .trident + .get_token_account(additional_tokens_recipient_token_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + let claim_additional_token_allocation = + launchpad_v_7::ClaimAdditionalTokenAllocationInstruction::data( + launchpad_v_7::ClaimAdditionalTokenAllocationInstructionData::new(), + ) + .accounts( + launchpad_v_7::ClaimAdditionalTokenAllocationInstructionAccounts::new( + launch, + payer, + launch_signer, + base_vault, + base_mint, + additional_tokens_recipient, + additional_tokens_recipient_token_account, + SOLANA_PROGRAM_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + event_authority, + launchpad_v_7::program_id(), + ), + ) + .instruction(); + + let res = self + .trident + .process_transaction(&[claim_additional_token_allocation], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_claim_additional_token_allocation_invariants( + launch, + launch_signer, + base_vault, + base_mint, + additional_tokens_recipient, + additional_tokens_recipient_token_account, + &pre_launch, + pre_base_vault_amount, + pre_recipient_amount, + ); + } + + pub fn initialize_performance_package( + &mut self, + launch: Pubkey, + payer: Pubkey, + launch_signer: Pubkey, + launch_base_vault: Pubkey, + base_mint: Pubkey, + dao: Pubkey, + squads_multisig: Pubkey, + squads_multisig_vault: Pubkey, + performance_package: Pubkey, + performance_package_token_account: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + let price_based_performance_package_event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before InitializePerformancePackage"); + let pre_launch_base_vault_amount = self + .trident + .get_token_account(launch_base_vault) + .expect( + "launch_base_vault token account must exist before InitializePerformancePackage", + ) + .account + .amount; + let pre_pp_vault_amount = self + .trident + .get_token_account(performance_package_token_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + let initialize_performance_package = + launchpad_v_7::InitializePerformancePackageInstruction::data( + launchpad_v_7::InitializePerformancePackageInstructionData::new(), + ) + .accounts( + launchpad_v_7::InitializePerformancePackageInstructionAccounts::new( + launch, + payer, + launch_signer, + launch_base_vault, + base_mint, + dao, + squads_multisig, + squads_multisig_vault, + performance_package, + performance_package_token_account, + SOLANA_PROGRAM_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + SQUADS_PROGRAM_ID, + price_based_performance_package::program_id(), + price_based_performance_package_event_authority, + event_authority, + launchpad_v_7::program_id(), + ), + ) + .instruction(); + + let res = self + .trident + .process_transaction(&[initialize_performance_package], message); + + // Verify invariants + if !res.is_success() { + return; + } + self.verify_initialize_performance_package_invariants( + launch, + launch_signer, + launch_base_vault, + base_mint, + dao, + squads_multisig_vault, + performance_package, + performance_package_token_account, + &pre_launch, + pre_launch_base_vault_amount, + pre_pp_vault_amount, + ); + } + + pub fn complete_launch( + &mut self, + launch: Pubkey, + launch_authority: Pubkey, + token_metadata: Pubkey, + payer: Pubkey, + launch_signer: Pubkey, + launch_quote_vault: Pubkey, + launch_base_vault: Pubkey, + base_mint: Pubkey, + quote_mint: Pubkey, + fee_recipient: Pubkey, + message: Option<&str>, + ) { + let event_authority = + get_event_authority_pda(&mut self.trident, launchpad_v_7::program_id()); + let autocrat_event_authority = + get_event_authority_pda(&mut self.trident, futarchy::program_id()); + let bid_wall_event_authority = + get_event_authority_pda(&mut self.trident, bid_wall::program_id()); + let damm_v2_event_authority = + get_event_authority_pda(&mut self.trident, DAMM_V2_PROGRAM_ID); + let squads_program_config = SQUADS_PROGRAM_CONFIG_ID; + let squads_program_config_treasury = SQUADS_PROGRAM_CONFIG_TREASURY_ID; + let pool_creator_authority = get_pool_creator_authority_pda(&mut self.trident); + let pool_authority = get_pool_authority_pda(&mut self.trident); + let config = METEORA_CONFIG_ID; + let position_nft_mint = get_position_nft_mint_pda(&mut self.trident, base_mint); + let pool = get_pool_pda(&mut self.trident, config, base_mint, quote_mint); + let position_nft_account = + get_position_nft_account_pda(&mut self.trident, position_nft_mint); + let position = get_position_pda(&mut self.trident, position_nft_mint); + let token_a_vault = get_token_a_vault_pda(&mut self.trident, base_mint, pool); + let token_b_vault = get_token_b_vault_pda(&mut self.trident, quote_mint, pool); + let dao = get_dao_pda(&mut self.trident, launch_signer, 0); + let squads_multisig = get_squads_multisig_pda(&mut self.trident, dao); + let squads_multisig_vault = + get_squads_multisig_vault_pda(&mut self.trident, squads_multisig); + let spending_limit = + get_squads_multisig_spending_limit_pda(&mut self.trident, squads_multisig, dao); + let treasury_quote_account = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + quote_mint, + squads_multisig_vault, + ); + let futarchy_amm_base_vault = + self.trident + .get_associated_token_address(&base_mint, &dao, &TOKEN_PROGRAM_ID); + let futarchy_amm_quote_vault = + self.trident + .get_associated_token_address("e_mint, &dao, &TOKEN_PROGRAM_ID); + let bid_wall = get_bid_wall_pda(&mut self.trident, base_mint, launch_signer); + let bid_wall_quote_token_account = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + quote_mint, + bid_wall, + ); + let dao_owned_lp_position = + get_amm_position_pda(&mut self.trident, dao, squads_multisig_vault); + + // Capture pre-state for invariants + let pre_launch = self + .trident + .get_account_with_type::(&launch, Some(8)) + .expect("Launch account must exist before CompleteLaunch"); + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_launch_quote_amount = self + .trident + .get_token_account(launch_quote_vault) + .expect("launch_quote_vault must exist before CompleteLaunch") + .account + .amount; + let pre_launch_base_amount = self + .trident + .get_token_account(launch_base_vault) + .expect("launch_base_vault must exist before CompleteLaunch") + .account + .amount; + let pre_treasury_quote_amount = self + .trident + .get_token_account(treasury_quote_account) + .expect("treasury_quote_account must exist before CompleteLaunch") + .account + .amount; + let pre_bid_wall_quote_amount = self + .trident + .get_token_account(bid_wall_quote_token_account) + .expect("bid_wall_quote_token_account must exist before CompleteLaunch") + .account + .amount; + + let static_accounts = StaticAccountsInstructionAccounts::new( + futarchy::program_id(), + MPL_TOKEN_METADATA_PROGRAM_ID, + autocrat_event_authority, + SQUADS_PROGRAM_ID, + squads_program_config, + squads_program_config_treasury, + bid_wall::program_id(), + bid_wall_event_authority, + ); + let meteora_accounts = MeteoraAccountsInstructionAccounts::new( + DAMM_V2_PROGRAM_ID, + config, + TOKEN_2022_PROGRAM_ID, + position_nft_account, + pool, + position, + position_nft_mint, + base_mint, + quote_mint, + token_a_vault, + token_b_vault, + pool_creator_authority, + pool_authority, + damm_v2_event_authority, + ); + let complete_launch = launchpad_v_7::CompleteLaunchInstruction::data( + launchpad_v_7::CompleteLaunchInstructionData::new(), + ) + .accounts(launchpad_v_7::CompleteLaunchInstructionAccounts::new( + launch, + launch_authority, + token_metadata, + payer, + launch_signer, + launch_quote_vault, + launch_base_vault, + treasury_quote_account, + base_mint, + quote_mint, + dao_owned_lp_position, + futarchy_amm_base_vault, + futarchy_amm_quote_vault, + dao, + squads_multisig, + squads_multisig_vault, + spending_limit, + bid_wall, + bid_wall_quote_token_account, + fee_recipient, + SOLANA_PROGRAM_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + static_accounts, + meteora_accounts, + event_authority, + launchpad_v_7::program_id(), + )) + .instruction(); + + let request_heap = ComputeBudgetInstruction::request_heap_frame((8 * 32 * 1024) as u32); + let res = self + .trident + .process_transaction(&[request_heap, complete_launch], message); + + if !res.is_success() { + return; + } + self.verify_complete_launch_invariants( + launch, + launch_authority, + dao, + squads_multisig_vault, + launch_quote_vault, + launch_base_vault, + treasury_quote_account, + bid_wall_quote_token_account, + &pre_launch, + pre_launch_quote_amount, + pre_launch_base_amount, + pre_treasury_quote_amount, + pre_bid_wall_quote_amount, + timestamp_before_tx, + ); + } +} diff --git a/trident-tests/fuzz_launchpad/methods/mod.rs b/trident-tests/fuzz_launchpad/methods/mod.rs new file mode 100644 index 000000000..5185e7bf9 --- /dev/null +++ b/trident-tests/fuzz_launchpad/methods/mod.rs @@ -0,0 +1,305 @@ +use crate::common::constants::MPL_TOKEN_METADATA_PROGRAM_ID; +use crate::common::types::launchpad_v_7; +use crate::common::types::launchpad_v_7::InitializeLaunchArgs; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::pda::get_launch_signer_pda; +use crate::common::pda::get_token_metadata_pda; +use crate::common::token::initialize_associated_token_account; + +pub mod launchpad; + +impl FuzzTest { + fn mostly_valid_99(&mut self) -> bool { + self.trident.random_from_range(1u8..=100u8) != 1 + } + + fn random_ascii_lower_string(&mut self, min_len: usize, max_len: usize) -> String { + let min_len_u32 = min_len as u32; + let max_len_u32 = max_len as u32; + let len = self.trident.random_from_range(min_len_u32..=max_len_u32) as usize; + (0..len) + .map(|_| { + let b = self.trident.random_from_range(b'a'..=b'z'); + b as char + }) + .collect() + } + + /// Generates `InitializeLaunchArgs` with constraints satisfied most of the time. + /// + /// Note: Our generated instruction builder always includes `additionalTokensRecipient`, + /// so "valid" generation must set `additional_tokens_amount > 0` (otherwise the on-chain + /// validation rejects). + pub fn random_initialize_launch_args( + &mut self, + performance_package_grantee: Pubkey, + team_address: Pubkey, + ) -> InitializeLaunchArgs { + const DAY: u32 = 60 * 60 * 24; + const MAX_SECONDS_FOR_LAUNCH: u32 = 14 * DAY; + const MIN_MONTHS_UNTIL_UNLOCK: u8 = 18; + const MAX_PREMINE: u64 = 15_000_000 * 1_000_000; + const MIN_QUOTE_LIQUIDITY: u64 = 100_000; + const MIN_MINIMUM_RAISE: u64 = MIN_QUOTE_LIQUIDITY * 5; + + let mostly_valid = self.mostly_valid_99(); + + // secondsForLaunch: valid is [1, 14 days] (and usually short to make CloseLaunch reachable) + let seconds_for_launch: u32 = if mostly_valid { + self.trident.random_from_range(60u32..=6 * 60 * 60) // 1 minute .. 6 hours + } else { + match self.trident.random_from_range(0u8..=4u8) { + 0 => 0, + 1 => 1, + 2 => MAX_SECONDS_FOR_LAUNCH + 1, + 3 => u32::MAX, + _ => self + .trident + .random_from_range(60u32..=MAX_SECONDS_FOR_LAUNCH), + } + }; + + // minimumRaiseAmount must be > 0, >= MIN_MINIMUM_RAISE, and >= monthlySpendingLimitAmount*6. + let minimum_raise_amount: u64 = if mostly_valid { + self.trident + .random_from_range(MIN_MINIMUM_RAISE..=5_000_000u64) // 0.5 .. 5_000 USDC + } else { + match self.trident.random_from_range(0u8..=4u8) { + 0 => 0, + 1 => MIN_MINIMUM_RAISE.saturating_sub(1), + 2 => 1, + 3 => u64::MAX, + _ => self + .trident + .random_from_range(MIN_MINIMUM_RAISE..=5_000_000_000u64), + } + }; + + // monthlySpendingLimitAmount must be != 0 and satisfy *6 <= minimumRaiseAmount. + let max_monthly = minimum_raise_amount.saturating_div(6).max(1); + let monthly_spending_limit_amount: u64 = self.trident.random_from_range(1u64..=max_monthly); + + // monthlySpendingLimitMembers: valid len is 0..=10, but empty is fine on-chain. + // Keep mostly non-empty to exercise CPI path in CompleteLaunch. + let member_count: usize = if mostly_valid { + self.trident.random_from_range(1u8..=3u8) as usize + } else { + match self.trident.random_from_range(0u8..=2u8) { + 0 => 0, + 1 => 11 + self.trident.random_from_range(0u8..=5u8) as usize, + _ => 1, + } + }; + let mut members = Vec::with_capacity(member_count); + for _ in 0..member_count { + members.push(self.trident.random_keypair().pubkey()); + } + + // Token metadata strings: keep within typical MPL constraints most of the time. + let token_name = if mostly_valid { + // <= 32 chars + self.random_ascii_lower_string(1, 16) + } else { + match self.trident.random_from_range(0u8..=2u8) { + 0 => "".to_string(), + 1 => self.random_ascii_lower_string(33, 64), // likely too long + _ => self.random_ascii_lower_string(1, 16), + } + }; + let token_symbol = if mostly_valid { + // <= 10 chars + self.random_ascii_lower_string(1, 6).to_uppercase() + } else { + match self.trident.random_from_range(0u8..=2u8) { + 0 => "".to_string(), + 1 => self.random_ascii_lower_string(11, 20).to_uppercase(), + _ => self.random_ascii_lower_string(1, 6).to_uppercase(), + } + }; + let token_uri = if mostly_valid { + format!( + "https://example.com/{}", + self.random_ascii_lower_string(4, 12) + ) + } else { + match self.trident.random_from_range(0u8..=2u8) { + 0 => "".to_string(), + 1 => self.random_ascii_lower_string(200, 400), // likely too long / invalid URI + _ => format!( + "https://example.com/{}", + self.random_ascii_lower_string(4, 12) + ), + } + }; + + // performancePackageTokenAmount: valid is [10, MAX_PREMINE], and monthsUntilInsidersCanUnlock >= 18. + let performance_package_token_amount: u64 = if mostly_valid { + self.trident + .random_from_range(10_000u64..=1_000_000_000_000u64) + .clamp(10, MAX_PREMINE) + .max(10) + } else { + match self.trident.random_from_range(0u8..=4u8) { + 0 => 0, + 1 => 9, + 2 => MAX_PREMINE.saturating_add(1), + 3 => u64::MAX, + _ => self + .trident + .random_from_range(10_000u64..=1_000_000_000_000u64) + .clamp(10, MAX_PREMINE) + .max(10), + } + }; + + let months_until_insiders_can_unlock: u8 = if mostly_valid { + self.trident + .random_from_range(MIN_MONTHS_UNTIL_UNLOCK..=36u8) + } else { + match self.trident.random_from_range(0u8..=3u8) { + 0 => 0, + 1 => MIN_MONTHS_UNTIL_UNLOCK - 1, + 2 => 255, + _ => self + .trident + .random_from_range(MIN_MONTHS_UNTIL_UNLOCK..=36u8), + } + }; + + // additionalTokensAmount: "valid" must be > 0 due to the always-present optional account in our builder. + let additional_tokens_amount: u64 = if mostly_valid { + self.trident.random_from_range(1u64..=5_000_000_000u64) + } else { + match self.trident.random_from_range(0u8..=3u8) { + 0 => 0, // should fail (recipient always provided) + 1 => u64::MAX, + _ => self.trident.random_from_range(1u64..=5_000_000_000u64), + } + }; + + let accumulator_activation_delay_seconds: u32 = if mostly_valid { + self.trident.random_from_range(0u32..=seconds_for_launch) + } else { + match self.trident.random_from_range(0u8..=3u8) { + 0 => 0, + 1 => MAX_SECONDS_FOR_LAUNCH.saturating_add(1), + 2 => u32::MAX, + _ => self + .trident + .random_from_range(0u32..=MAX_SECONDS_FOR_LAUNCH), + } + }; + + InitializeLaunchArgs::new( + minimum_raise_amount, + monthly_spending_limit_amount, + members, + seconds_for_launch, + token_name, + token_symbol, + token_uri, + performance_package_grantee, + performance_package_token_amount, + months_until_insiders_can_unlock, + team_address, + additional_tokens_amount, + accumulator_activation_delay_seconds, + ) + } + + pub fn random_fund_amount(&mut self, funder_balance: u64) -> u64 { + // 99% valid-ish, 1% invalid-ish (0 or > balance) + let mostly_valid = self.mostly_valid_99(); + if mostly_valid { + if funder_balance == 0 { + return 0; + } + let upper = funder_balance.min(2_000_000_000); + self.trident.random_from_range(1u64..=upper.max(1)) + } else { + match self.trident.random_from_range(0u8..=4u8) { + 0 => 0, + 1 => funder_balance.saturating_add(1), + 2 => u64::MAX, + 3 => self.trident.random_from_range(0u64..=10u64), + _ => { + if funder_balance == 0 { + 1 + } else { + self.trident.random_from_range(1u64..=funder_balance) + } + } + } + } + } + + pub fn random_approved_amount(&mut self, committed_amount: u64) -> u64 { + // 99% within bounds, 1% out-of-bounds + let mostly_valid = self.mostly_valid_99(); + if mostly_valid { + self.trident.random_from_range(0u64..=committed_amount) + } else { + match self.trident.random_from_range(0u8..=3u8) { + 0 => committed_amount.saturating_add(1), + 1 => u64::MAX, + 2 => committed_amount.saturating_add(10), + _ => self.trident.random_from_range(0u64..=committed_amount), + } + } + } + + pub fn initial_setup(&mut self) { + self.trident + .airdrop(&self.payer.pubkey(), 500 * LAMPORTS_PER_SOL); + } + + pub fn initialize_token_metadata(&mut self, base_mint: Pubkey) -> Pubkey { + let token_metadata = get_token_metadata_pda(&mut self.trident, base_mint); + self.trident.create_account( + &self.payer.pubkey(), + &token_metadata, + 1000000, + 10000, + &MPL_TOKEN_METADATA_PROGRAM_ID, + ); + token_metadata + } + + pub fn initialize_launch_signer(&mut self, launch: Pubkey) -> Pubkey { + let launch_signer = get_launch_signer_pda(&mut self.trident, launch); + self.trident.create_account( + &self.payer.pubkey(), + &launch_signer, + 1000000, + 10000, + &launchpad_v_7::program_id(), + ); + launch_signer + } + + pub fn setup_funder_accounts( + &mut self, + quote_mint: Pubkey, + funder_token_amount: u64, + mint_authority: Pubkey, + ) -> (Pubkey, Pubkey) { + let funder = self.trident.random_keypair(); + let funder_quote_account = initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + quote_mint, + funder.pubkey(), + ); + let mint_to_funder_quote_account_ix = self.trident.mint_to( + &funder_quote_account, + "e_mint, + &mint_authority, + funder_token_amount, + ); + self.trident + .process_transaction(&[mint_to_funder_quote_account_ix], None); + (funder.pubkey(), funder_quote_account) + } +} diff --git a/trident-tests/fuzz_launchpad/test_fuzz.rs b/trident-tests/fuzz_launchpad/test_fuzz.rs new file mode 100644 index 000000000..b629fd962 --- /dev/null +++ b/trident-tests/fuzz_launchpad/test_fuzz.rs @@ -0,0 +1,677 @@ +use fuzz_accounts::*; +use trident_fuzz::fuzzing::*; + +#[path = "../common/mod.rs"] +pub mod common; +mod fuzz_accounts; +pub mod invariants; +pub mod methods; + +use crate::common::constants::FEE_RECIPIENT_ID; +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::constants::USDC_MINT; +use crate::common::pda::get_funding_record_pda; +use crate::common::pda::get_launchpad_pda; +use crate::common::pda::get_performance_package_pda; +use crate::common::pda::get_squads_multisig_pda; +use crate::common::pda::get_squads_multisig_vault_pda; +use crate::common::token::get_or_initialize_associated_token_account; +use crate::common::token::initialize_mint; +use crate::common::types::launchpad_v_7::FundingRecord; +use crate::common::types::launchpad_v_7::Launch; +use crate::common::types::launchpad_v_7::LaunchState; + +#[derive(FuzzTestMethods)] +struct FuzzTest { + /// Trident client for interacting with the Solana program + trident: Trident, + /// Storage for all account addresses used in fuzz testing + fuzz_accounts: AccountAddresses, + + // ============================================================================ + // Accounts + payer: Keypair, + // ============================================================================ +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + + let payer = trident.random_keypair(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + payer, + } + } + + #[init] + fn start(&mut self) { + self.initial_setup(); + + let base_mint = self.fuzz_accounts.baseMint.insert(&mut self.trident, None); + let quote_mint = USDC_MINT; + self.fuzz_accounts.quoteMint.insert_with_address(quote_mint); + + let launch = get_launchpad_pda(&mut self.trident, base_mint); + let launch_signer = self.initialize_launch_signer(launch); + + let launch_authority = self.trident.random_keypair(); + let wrong_authority = self.trident.random_keypair(); + self.trident + .airdrop(&launch_authority.pubkey(), 50 * LAMPORTS_PER_SOL); + self.trident + .airdrop(&wrong_authority.pubkey(), 50 * LAMPORTS_PER_SOL); + + // Base mint: authority must be launch_signer PDA. + initialize_mint( + &mut self.trident, + self.payer.pubkey(), + base_mint, + 6, + launch_signer, + None, + None, + ); + + initialize_mint( + &mut self.trident, + self.payer.pubkey(), + quote_mint, + 6, + self.payer.pubkey(), + None, + None, + ); + + // Create metadata PDA account (needed by MPL CPI). + let token_metadata = self.initialize_token_metadata(base_mint); + + // Pre-create vault ATAs to ensure they exist for later instructions (Fund expects them). + let quote_vault = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + quote_mint, + launch_signer, + ); + let base_vault = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + base_mint, + launch_signer, + ); + + // Recipients / misc addresses. + let additional_tokens_recipient = self.payer.pubkey(); + let performance_package_grantee = self.payer.pubkey(); + let team_address = self.trident.random_pubkey(); + + // Initialize launch (mostly-valid, occasionally-invalid to test rejects). + let args = self.random_initialize_launch_args(performance_package_grantee, team_address); + self.initialize_launch( + self.payer.pubkey(), + launch, + base_mint, + quote_mint, + token_metadata, + launch_signer, + quote_vault, + base_vault, + launch_authority.pubkey(), + additional_tokens_recipient, + args, + Some("Init: InitializeLaunch"), + ); + + // Persist address pools for flows + self.fuzz_accounts.launch.insert_with_address(launch); + self.fuzz_accounts + .launchSigner + .insert_with_address(launch_signer); + self.fuzz_accounts + .tokenMetadata + .insert_with_address(token_metadata); + self.fuzz_accounts + .launchAuthority + .insert_with_address(launch_authority.pubkey()); + self.fuzz_accounts + .launchAuthority + .insert_with_address(wrong_authority.pubkey()); + self.fuzz_accounts + .additionalTokensRecipient + .insert_with_address(additional_tokens_recipient); + + // Funders: 2-5, each with a quote ATA seeded with a balance. + let funder_count = 2; + let funder_token_amount = 1_000_000_000_000; // 1M USDC + for _ in 0..funder_count { + let (funder, _funder_quote_account) = + self.setup_funder_accounts(quote_mint, funder_token_amount, self.payer.pubkey()); + self.fuzz_accounts.funder.insert_with_address(funder); + self.trident.airdrop(&funder, 10 * LAMPORTS_PER_SOL); + + // Pre-create base ATA for claims (idempotent). + get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + base_mint, + funder, + ); + } + + self.fuzz_accounts + .feeRecipient + .insert_with_address(FEE_RECIPIENT_ID); + } + + #[flow] + fn flow1_start_or_fund(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + match launch_acc.state { + LaunchState::Initialized => { + // 99.9% correct authority, 0.1% wrong signer. + let authority = if self.trident.random_from_range(1u16..=1000u16) != 1 { + launch_acc.launchAuthority + } else { + self.trident.random_keypair().pubkey() + }; + + self.start_launch(launch, authority, Some("Flow1: StartLaunch")); + } + LaunchState::Live => { + let funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let other_funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + + let funding_record = if self.trident.random_from_range(1u8..=100u8) != 1 { + get_funding_record_pda(&mut self.trident, launch, funder) + } else { + // Wrong but existing PDA (most of the time) + get_funding_record_pda(&mut self.trident, launch, other_funder) + }; + + let correct_funder_quote_account = self.trident.get_associated_token_address( + &launch_acc.quoteMint, + &funder, + &TOKEN_PROGRAM_ID, + ); + let wrong_funder_quote_account = self.trident.get_associated_token_address( + &launch_acc.quoteMint, + &other_funder, + &TOKEN_PROGRAM_ID, + ); + + let funder_quote_account = if self.trident.random_from_range(1u8..=100u8) != 1 { + correct_funder_quote_account + } else { + wrong_funder_quote_account + }; + + let funder_balance = self + .trident + .get_token_account(correct_funder_quote_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + let amount = self.random_fund_amount(funder_balance); + + self.fund( + self.payer.pubkey(), + launch, + funding_record, + launch_acc.launchSigner, + launch_acc.launchQuoteVault, + funder, + funder_quote_account, + amount, + Some("Flow1: Fund"), + ); + + if self + .trident + .get_account_with_type::(&funding_record, Some(8)) + .is_some() + { + self.fuzz_accounts + .fundingRecord + .insert_with_address(funding_record); + } + } + _ => { + // In other states, avoid spamming meaningless failures (but rarely still try a Fund). + if self.trident.random_from_range(1u16..=1000u16) != 1 { + return; + } + let funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let funder_quote_account = self.trident.get_associated_token_address( + &launch_acc.quoteMint, + &funder, + &TOKEN_PROGRAM_ID, + ); + let funder_balance = self + .trident + .get_token_account(funder_quote_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + let amount = self.random_fund_amount(funder_balance); + let funding_record = get_funding_record_pda(&mut self.trident, launch, funder); + self.fund( + self.payer.pubkey(), + launch, + funding_record, + launch_acc.launchSigner, + launch_acc.launchQuoteVault, + funder, + funder_quote_account, + amount, + Some("Flow1: Fund (state mismatch)"), + ); + } + } + } + + #[flow] + fn flow2_close_launch_and_set_funding_record_approval(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!(launch_acc.state, LaunchState::Live) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let mostly_valid = self.trident.random_from_range(1u8..=100u8) != 1; + // Bias toward allowing Fund to run a bit before closing, so we reach deeper states more often. + if mostly_valid + && matches!(launch_acc.state, LaunchState::Live) + && launch_acc.totalCommittedAmount < launch_acc.minimumRaiseAmount + && self.trident.random_from_range(0u8..=4u8) != 0 + { + return; + } + if mostly_valid { + if let Some(started) = launch_acc.unixTimestampStarted { + let period_end = started.saturating_add(launch_acc.secondsForLaunch as i64); + let now = self.trident.get_current_timestamp(); + if now < period_end { + self.trident.forward_in_time((period_end - now) + 1); + } + } + } + + self.close_launch(launch, Some("Flow2: CloseLaunch")); + + let mut fr_acc: Option = None; + let mut fr: Option = None; + while fr_acc.is_none() { + fr = self.fuzz_accounts.fundingRecord.get(&mut self.trident); + if fr.is_none() { + return; + } + let fr = fr.unwrap(); + fr_acc = self + .trident + .get_account_with_type::(&fr, Some(8)); + } + + if fr_acc.is_none() { + return; + } + let approved_amount = fr_acc.unwrap().committedAmount; + self.set_funding_record_approval( + launch, + fr.unwrap(), + launch_acc.launchAuthority, + approved_amount, + Some("Flow2: SetFundingRecordApproval"), + ); + } + + #[flow] + fn flow3_set_funding_record_approval(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!(launch_acc.state, LaunchState::Closed) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let fr = get_funding_record_pda(&mut self.trident, launch, funder); + let fr_acc = self + .trident + .get_account_with_type::(&fr, Some(8)); + if fr_acc.is_none() { + return; + } + let fr_acc = fr_acc.unwrap(); + + // 99.9% correct authority, 0.1% wrong signer. + let authority = if self.trident.random_from_range(1u16..=1000u16) != 1 { + launch_acc.launchAuthority + } else { + self.fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set") + }; + + let approved_amount = self.random_approved_amount(fr_acc.committedAmount); + self.set_funding_record_approval( + launch, + fr, + authority, + approved_amount, + Some("Flow3: SetFundingRecordApproval"), + ); + } + + #[flow] + fn flow4_complete_launch(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!(launch_acc.state, LaunchState::Closed) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let mostly_valid = self.trident.random_from_range(0u8..=99u8) != 0; // 99% + + let token_metadata = self + .fuzz_accounts + .tokenMetadata + .get(&mut self.trident) + .expect("tokenMetadata must be set"); + let fee_recipient = self + .fuzz_accounts + .feeRecipient + .get(&mut self.trident) + .expect("feeRecipient must be set"); + + let launch_authority = if mostly_valid { + launch_acc.launchAuthority + } else { + self.trident.random_keypair().pubkey() + }; + + self.complete_launch( + launch, + launch_authority, + token_metadata, + self.payer.pubkey(), + launch_acc.launchSigner, + launch_acc.launchQuoteVault, + launch_acc.launchBaseVault, + launch_acc.baseMint, + launch_acc.quoteMint, + fee_recipient, + Some("Flow4: CompleteLaunch"), + ); + } + + #[flow] + fn flow5_refund(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!( + launch_acc.state, + LaunchState::Refunding | LaunchState::Complete + ) && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let fr = get_funding_record_pda(&mut self.trident, launch, funder); + + let correct_funder_quote_account = self.trident.get_associated_token_address( + &launch_acc.quoteMint, + &funder, + &TOKEN_PROGRAM_ID, + ); + let other_funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let wrong_funder_quote_account = self.trident.get_associated_token_address( + &launch_acc.quoteMint, + &other_funder, + &TOKEN_PROGRAM_ID, + ); + let funder_quote_account = if self.trident.random_from_range(1u8..=100u8) != 1 { + correct_funder_quote_account + } else { + wrong_funder_quote_account + }; + + self.refund( + launch, + fr, + launch_acc.launchQuoteVault, + launch_acc.launchSigner, + funder, + funder_quote_account, + Some("Flow5: Refund"), + ); + } + + #[flow] + fn flow6_claim(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!(launch_acc.state, LaunchState::Complete) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let fr = get_funding_record_pda(&mut self.trident, launch, funder); + + let correct_funder_base_account = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + launch_acc.baseMint, + funder, + ); + let other_funder = self + .fuzz_accounts + .funder + .get(&mut self.trident) + .expect("funder must be set"); + let wrong_funder_base_account = self.trident.get_associated_token_address( + &launch_acc.baseMint, + &other_funder, + &TOKEN_PROGRAM_ID, + ); + let funder_base_account = if self.trident.random_from_range(1u8..=100u8) != 1 { + correct_funder_base_account + } else { + wrong_funder_base_account + }; + + self.claim( + launch, + fr, + launch_acc.launchSigner, + launch_acc.baseMint, + launch_acc.launchBaseVault, + funder, + funder_base_account, + Some("Flow6: Claim"), + ); + } + + #[flow] + fn flow7_post_complete_extras(&mut self) { + let launch = self + .fuzz_accounts + .launch + .get(&mut self.trident) + .expect("launch must be set"); + let Some(launch_acc) = self + .trident + .get_account_with_type::(&launch, Some(8)) + else { + return; + }; + + if !matches!(launch_acc.state, LaunchState::Complete) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + // 50/50 between "additional tokens" and "performance package" work. + if self.trident.random_from_range(0u8..=1u8) == 0 { + // Claim additional tokens if configured and not yet claimed. + if launch_acc.additionalTokensAmount > 0 + && !launch_acc.additionalTokensClaimed + && launch_acc.additionalTokensRecipient.is_some() + { + let recipient = launch_acc.additionalTokensRecipient.unwrap(); + let recipient_token_account = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + launch_acc.baseMint, + recipient, + ); + + self.claim_additional_token_allocation( + launch, + self.payer.pubkey(), + launch_acc.launchSigner, + launch_acc.launchBaseVault, + launch_acc.baseMint, + recipient, + recipient_token_account, + Some("Flow7: ClaimAdditionalTokenAllocation"), + ); + } + return; + } + + // Initialize performance package if possible. + if !launch_acc.isPerformancePackageInitialized && launch_acc.dao.is_some() { + let dao = launch_acc.dao.unwrap(); + let squads_multisig = get_squads_multisig_pda(&mut self.trident, dao); + let squads_multisig_vault = + get_squads_multisig_vault_pda(&mut self.trident, squads_multisig); + let performance_package = + get_performance_package_pda(&mut self.trident, launch_acc.launchSigner); + let performance_package_token_account = get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + launch_acc.baseMint, + performance_package, + ); + + self.initialize_performance_package( + launch, + self.payer.pubkey(), + launch_acc.launchSigner, + launch_acc.launchBaseVault, + launch_acc.baseMint, + dao, + squads_multisig, + squads_multisig_vault, + performance_package, + performance_package_token_account, + Some("Flow7: InitializePerformancePackage"), + ); + } + } +} + +fn main() { + FuzzTest::fuzz(10000, 100); +} diff --git a/trident-tests/fuzz_mint_governor/constants.rs b/trident-tests/fuzz_mint_governor/constants.rs new file mode 100644 index 000000000..784b75d49 --- /dev/null +++ b/trident-tests/fuzz_mint_governor/constants.rs @@ -0,0 +1,8 @@ +pub const FUZZ_ITERATIONS: u32 = 10_000; +pub const FLOWS_PER_ITERATION: u32 = 100; + +pub const MINT_DECIMALS: u8 = 6; +pub const SMALL_LIMIT: u64 = 1_000; +pub const LARGE_LIMIT: u64 = 5_000; +pub const SMALL_MINT: u64 = 100; +pub const LARGE_MINT: u64 = 750; diff --git a/trident-tests/fuzz_mint_governor/flows.rs b/trident-tests/fuzz_mint_governor/flows.rs new file mode 100644 index 000000000..90a265edf --- /dev/null +++ b/trident-tests/fuzz_mint_governor/flows.rs @@ -0,0 +1,108 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::Signer; + +impl FuzzTest { + pub fn manage_minter_flow(&mut self, message: &str) { + if !self.minter_exists { + let admin = if self.trident.random_from_range(0u8..=9u8) == 0 { + self.fake_admin.pubkey() + } else { + self.payer.pubkey() + }; + let max_total = self.sample_limit(); + + if self.add_mint_authority( + admin, + self.authorized_minter.pubkey(), + max_total, + Some(message), + ) { + self.minter_exists = true; + self.expected_max_total = max_total; + self.expected_total_minted = 0; + } + return; + } + + if self.trident.random_from_range(0u8..=9u8) < 7 { + let admin = if self.trident.random_from_range(0u8..=9u8) == 0 { + self.fake_admin.pubkey() + } else { + self.payer.pubkey() + }; + let max_total = self.sample_limit(); + + if self.update_mint_authority( + admin, + self.authorized_minter.pubkey(), + max_total, + Some(message), + ) { + self.expected_max_total = max_total; + } + } else { + let admin = if self.trident.random_from_range(0u8..=9u8) == 0 { + self.fake_admin.pubkey() + } else { + self.payer.pubkey() + }; + + if self.remove_mint_authority( + admin, + self.authorized_minter.pubkey(), + self.payer.pubkey(), + Some(message), + ) { + self.minter_exists = false; + self.expected_max_total = None; + self.expected_total_minted = 0; + } + } + } + + pub fn mint_flow(&mut self, message: &str) { + if !self.minter_exists { + return; + } + + let use_valid_signer = self.trident.random_from_range(0u8..=9u8) != 0; + let signer = if use_valid_signer { + self.authorized_minter.pubkey() + } else { + self.fake_minter.pubkey() + }; + + let amount = if use_valid_signer { + if self.trident.random_from_range(0u8..=9u8) < 7 { + match self.valid_mint_amount() { + Some(amount) => amount, + None => return, + } + } else { + match self.invalid_mint_amount() { + Some(amount) => amount, + None => match self.valid_mint_amount() { + Some(amount) => amount, + None => return, + }, + } + } + } else { + match self.valid_mint_amount() { + Some(amount) => amount, + None => return, + } + }; + + if self.mint_tokens( + self.authorized_minter.pubkey(), + signer, + self.recipient_ata, + amount, + Some(message), + ) + { + self.expected_total_minted = self.expected_total_minted.saturating_add(amount); + } + } +} diff --git a/trident-tests/fuzz_mint_governor/fuzz_accounts.rs b/trident-tests/fuzz_mint_governor/fuzz_accounts.rs new file mode 100644 index 000000000..bd2310b05 --- /dev/null +++ b/trident-tests/fuzz_mint_governor/fuzz_accounts.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct AccountAddresses {} diff --git a/trident-tests/fuzz_mint_governor/helpers.rs b/trident-tests/fuzz_mint_governor/helpers.rs new file mode 100644 index 000000000..b7917c720 --- /dev/null +++ b/trident-tests/fuzz_mint_governor/helpers.rs @@ -0,0 +1,111 @@ +use trident_fuzz::fuzzing::Signer; +use trident_fuzz::fuzzing::*; + +use crate::common::pda::get_event_authority_pda; +use crate::common::token::get_or_initialize_associated_token_account; +use crate::common::types::mint_governor::MintAuthority; +use crate::common::types::mint_governor::MintGovernor; +use crate::common::types::mint_governor::{self}; +use crate::constants::*; +use crate::FuzzTest; + +impl FuzzTest { + pub fn mint_governor_pda(&mut self) -> Pubkey { + self.trident + .find_program_address( + &[ + b"mint_governor", + self.mint.as_ref(), + self.create_key.pubkey().as_ref(), + ], + &mint_governor::program_id(), + ) + .0 + } + + pub fn mint_authority_pda(&mut self, authorized_minter: Pubkey) -> Pubkey { + self.trident + .find_program_address( + &[ + b"mint_authority", + self.mint_governor.as_ref(), + authorized_minter.as_ref(), + ], + &mint_governor::program_id(), + ) + .0 + } + + pub fn mint_governor_event_authority(&mut self) -> Pubkey { + get_event_authority_pda(&mut self.trident, mint_governor::program_id()) + } + + pub fn read_governor(&mut self) -> MintGovernor { + self.trident + .get_account_with_type::(&self.mint_governor, Some(8)) + .expect("mint governor must exist") + } + + pub fn read_mint_authority(&mut self, authorized_minter: Pubkey) -> Option { + let mint_authority = self.mint_authority_pda(authorized_minter); + self.trident + .get_account_with_type::(&mint_authority, Some(8)) + } + + pub fn token_balance_for_ata(&mut self, ata: Pubkey) -> u64 { + self.trident + .get_token_account(ata) + .map(|account| account.account.amount) + .unwrap_or(0) + } + + pub fn recipient_ata_for_owner(&mut self, owner: Pubkey) -> Pubkey { + get_or_initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + self.mint, + owner, + ) + } + + pub fn sample_limit(&mut self) -> Option { + match self.trident.random_from_range(0u8..=3u8) { + 0 => Some(SMALL_LIMIT), + 1 => Some(LARGE_LIMIT), + 2 => Some(self.expected_total_minted), + _ => None, + } + } + + pub fn valid_mint_amount(&mut self) -> Option { + match self.expected_max_total { + Some(limit) => { + let remaining = limit.saturating_sub(self.expected_total_minted); + if remaining == 0 { + None + } else { + Some(match self.trident.random_from_range(0u8..=2u8) { + 0 => 1, + 1 => remaining.min(SMALL_MINT), + _ => remaining, + }) + } + } + None => Some(if self.trident.random_from_range(0u8..=1u8) == 0 { + SMALL_MINT + } else { + LARGE_MINT + }), + } + } + + pub fn invalid_mint_amount(&mut self) -> Option { + match self.expected_max_total { + Some(limit) => { + let remaining = limit.saturating_sub(self.expected_total_minted); + Some(remaining.saturating_add(1)) + } + None => None, + } + } +} diff --git a/trident-tests/fuzz_mint_governor/invariants.rs b/trident-tests/fuzz_mint_governor/invariants.rs new file mode 100644 index 000000000..e6e4fb53b --- /dev/null +++ b/trident-tests/fuzz_mint_governor/invariants.rs @@ -0,0 +1,26 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::Signer; + +impl FuzzTest { + pub fn assert_global_invariants(&mut self) { + let governor = self.read_governor(); + assert_eq!(governor.mint, self.mint); + assert_eq!(governor.createKey, self.create_key.pubkey()); + assert_eq!(governor.admin, self.payer.pubkey()); + + let account = self.read_mint_authority(self.authorized_minter.pubkey()); + if !self.minter_exists { + assert!( + account.is_none(), + "inactive mint authority should not exist" + ); + return; + } + + let account = account.expect("mint authority should exist"); + assert_eq!(account.mintGovernor, self.mint_governor); + assert_eq!(account.authorizedMinter, self.authorized_minter.pubkey()); + assert_eq!(account.maxTotal, self.expected_max_total); + assert_eq!(account.totalMinted, self.expected_total_minted); + } +} diff --git a/trident-tests/fuzz_mint_governor/methods.rs b/trident-tests/fuzz_mint_governor/methods.rs new file mode 100644 index 000000000..32808bfba --- /dev/null +++ b/trident-tests/fuzz_mint_governor/methods.rs @@ -0,0 +1,260 @@ +use trident_fuzz::fuzzing::Signer; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::SOLANA_PROGRAM_ID; +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::types::mint_governor; +use crate::common::types::mint_governor::AddMintAuthorityArgs; +use crate::common::types::mint_governor::AddMintAuthorityInstruction; +use crate::common::types::mint_governor::AddMintAuthorityInstructionAccounts; +use crate::common::types::mint_governor::AddMintAuthorityInstructionData; +use crate::common::types::mint_governor::InitializeMintGovernorInstruction; +use crate::common::types::mint_governor::InitializeMintGovernorInstructionAccounts; +use crate::common::types::mint_governor::InitializeMintGovernorInstructionData; +use crate::common::types::mint_governor::MintTokensArgs; +use crate::common::types::mint_governor::MintTokensInstruction; +use crate::common::types::mint_governor::MintTokensInstructionAccounts; +use crate::common::types::mint_governor::MintTokensInstructionData; +use crate::common::types::mint_governor::RemoveMintAuthorityInstruction; +use crate::common::types::mint_governor::RemoveMintAuthorityInstructionAccounts; +use crate::common::types::mint_governor::RemoveMintAuthorityInstructionData; +use crate::common::types::mint_governor::TransferAuthorityToGovernorInstruction; +use crate::common::types::mint_governor::TransferAuthorityToGovernorInstructionAccounts; +use crate::common::types::mint_governor::TransferAuthorityToGovernorInstructionData; +use crate::common::types::mint_governor::UpdateMintAuthorityArgs; +use crate::common::types::mint_governor::UpdateMintAuthorityInstruction; +use crate::common::types::mint_governor::UpdateMintAuthorityInstructionAccounts; +use crate::common::types::mint_governor::UpdateMintAuthorityInstructionData; +use crate::FuzzTest; + +impl FuzzTest { + pub fn airdrop_accounts(&mut self) { + for account in [ + self.payer.pubkey(), + self.create_key.pubkey(), + self.fake_admin.pubkey(), + self.authorized_minter.pubkey(), + self.fake_minter.pubkey(), + self.recipient.pubkey(), + ] { + self.trident.airdrop(&account, 50 * LAMPORTS_PER_SOL); + } + } + + pub fn initialize_mint_governor(&mut self, message: Option<&str>) -> bool { + let ix = + InitializeMintGovernorInstruction::data(InitializeMintGovernorInstructionData::new()) + .accounts(InitializeMintGovernorInstructionAccounts::new( + self.mint, + self.mint_governor, + self.create_key.pubkey(), + self.payer.pubkey(), + self.payer.pubkey(), + SOLANA_PROGRAM_ID, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[ix], message); + if !res.is_success() { + return false; + } + + let governor = self.read_governor(); + assert_eq!(governor.mint, self.mint); + assert_eq!(governor.admin, self.payer.pubkey()); + assert_eq!(governor.createKey, self.create_key.pubkey()); + assert_eq!(governor.seqNum, 0); + true + } + + pub fn transfer_authority_to_governor( + &mut self, + current_authority: Pubkey, + message: Option<&str>, + ) -> bool { + let ix = TransferAuthorityToGovernorInstruction::data( + TransferAuthorityToGovernorInstructionData::new(), + ) + .accounts(TransferAuthorityToGovernorInstructionAccounts::new( + self.mint_governor, + self.mint, + current_authority, + TOKEN_PROGRAM_ID, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + self.trident.process_transaction(&[ix], message).is_success() + } + + pub fn add_mint_authority( + &mut self, + admin: Pubkey, + authorized_minter: Pubkey, + max_total: Option, + message: Option<&str>, + ) -> bool { + let pre_mint_authority = self.read_mint_authority(authorized_minter); + + let ix = AddMintAuthorityInstruction::data(AddMintAuthorityInstructionData::new( + AddMintAuthorityArgs::new(max_total), + )) + .accounts(AddMintAuthorityInstructionAccounts::new( + self.mint_governor, + self.mint_authority_pda(authorized_minter), + admin, + authorized_minter, + self.payer.pubkey(), + SOLANA_PROGRAM_ID, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[ix], message); + let post_mint_authority = self.read_mint_authority(authorized_minter); + + if !res.is_success() { + assert_eq!(post_mint_authority, pre_mint_authority); + return false; + } + + let post_mint_authority = post_mint_authority.expect("mint authority must exist"); + assert_eq!(post_mint_authority.mintGovernor, self.mint_governor); + assert_eq!(post_mint_authority.authorizedMinter, authorized_minter); + assert_eq!(post_mint_authority.maxTotal, max_total); + assert_eq!(post_mint_authority.totalMinted, 0); + true + } + + pub fn update_mint_authority( + &mut self, + admin: Pubkey, + authorized_minter: Pubkey, + max_total: Option, + message: Option<&str>, + ) -> bool { + let Some(pre_mint_authority) = self.read_mint_authority(authorized_minter) else { + return false; + }; + + let ix = UpdateMintAuthorityInstruction::data(UpdateMintAuthorityInstructionData::new( + UpdateMintAuthorityArgs::new(max_total), + )) + .accounts(UpdateMintAuthorityInstructionAccounts::new( + self.mint_governor, + self.mint_authority_pda(authorized_minter), + admin, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[ix], message); + let post_mint_authority = self + .read_mint_authority(authorized_minter) + .expect("mint authority should still exist"); + + if !res.is_success() { + assert_eq!(post_mint_authority, pre_mint_authority); + return false; + } + + assert_eq!(post_mint_authority.authorizedMinter, authorized_minter); + assert_eq!( + post_mint_authority.totalMinted, + pre_mint_authority.totalMinted + ); + assert_eq!(post_mint_authority.maxTotal, max_total); + true + } + + pub fn remove_mint_authority( + &mut self, + admin: Pubkey, + authorized_minter: Pubkey, + rent_destination: Pubkey, + message: Option<&str>, + ) -> bool { + let Some(pre_mint_authority) = self.read_mint_authority(authorized_minter) else { + return false; + }; + + let ix = RemoveMintAuthorityInstruction::data(RemoveMintAuthorityInstructionData::new()) + .accounts(RemoveMintAuthorityInstructionAccounts::new( + self.mint_governor, + self.mint_authority_pda(authorized_minter), + admin, + rent_destination, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[ix], message); + let post_mint_authority = self.read_mint_authority(authorized_minter); + + if !res.is_success() { + assert_eq!(post_mint_authority, Some(pre_mint_authority)); + return false; + } + + assert!(post_mint_authority.is_none()); + true + } + + pub fn mint_tokens( + &mut self, + mint_authority_minter: Pubkey, + signer_minter: Pubkey, + destination_ata: Pubkey, + amount: u64, + message: Option<&str>, + ) -> bool { + let pre_destination_amount = self.token_balance_for_ata(destination_ata); + let pre_mint_authority = self.read_mint_authority(mint_authority_minter); + + let ix = MintTokensInstruction::data(MintTokensInstructionData::new(MintTokensArgs::new( + amount, + ))) + .accounts(MintTokensInstructionAccounts::new( + self.mint_governor, + self.mint_authority_pda(mint_authority_minter), + self.mint, + destination_ata, + signer_minter, + TOKEN_PROGRAM_ID, + self.mint_governor_event_authority(), + mint_governor::program_id(), + )) + .instruction(); + + let res = self.trident.process_transaction(&[ix], message); + let post_destination_amount = self.token_balance_for_ata(destination_ata); + let post_mint_authority = self.read_mint_authority(mint_authority_minter); + + if !res.is_success() { + assert_eq!(post_destination_amount, pre_destination_amount); + assert_eq!(post_mint_authority, pre_mint_authority); + return false; + } + + let pre_mint_authority = pre_mint_authority.expect("mint authority must exist"); + let post_mint_authority = post_mint_authority.expect("mint authority must exist"); + + assert_eq!(post_destination_amount, pre_destination_amount + amount); + assert_eq!( + post_mint_authority.totalMinted, + pre_mint_authority.totalMinted + amount + ); + + if let Some(max_total) = post_mint_authority.maxTotal { + assert!(post_mint_authority.totalMinted <= max_total); + } + + true + } +} diff --git a/trident-tests/fuzz_mint_governor/test_fuzz.rs b/trident-tests/fuzz_mint_governor/test_fuzz.rs new file mode 100644 index 000000000..da0d736e1 --- /dev/null +++ b/trident-tests/fuzz_mint_governor/test_fuzz.rs @@ -0,0 +1,117 @@ +use fuzz_accounts::*; +use trident_fuzz::fuzzing::Signer; +use trident_fuzz::fuzzing::*; + +#[path = "../common/mod.rs"] +pub mod common; + +mod constants; +mod flows; +mod fuzz_accounts; +mod helpers; +mod invariants; +mod methods; + +use crate::common::token::initialize_mint; +use constants::*; + +#[derive(FuzzTestMethods)] +pub struct FuzzTest { + trident: Trident, + fuzz_accounts: AccountAddresses, + + payer: Keypair, + create_key: Keypair, + fake_admin: Keypair, + authorized_minter: Keypair, + fake_minter: Keypair, + recipient: Keypair, + + mint: Pubkey, + mint_governor: Pubkey, + recipient_ata: Pubkey, + minter_exists: bool, + expected_max_total: Option, + expected_total_minted: u64, +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + + Self { + payer: trident.random_keypair(), + create_key: trident.random_keypair(), + fake_admin: trident.random_keypair(), + authorized_minter: trident.random_keypair(), + fake_minter: trident.random_keypair(), + recipient: trident.random_keypair(), + trident, + fuzz_accounts: AccountAddresses::default(), + mint: Pubkey::default(), + mint_governor: Pubkey::default(), + recipient_ata: Pubkey::default(), + minter_exists: false, + expected_max_total: None, + expected_total_minted: 0, + } + } + + #[init] + fn start(&mut self) { + self.airdrop_accounts(); + + self.mint = self.trident.random_keypair().pubkey(); + + initialize_mint( + &mut self.trident, + self.payer.pubkey(), + self.mint, + MINT_DECIMALS, + self.payer.pubkey(), + None, + Some("Init: Mint"), + ); + + self.mint_governor = self.mint_governor_pda(); + self.recipient_ata = self.recipient_ata_for_owner(self.recipient.pubkey()); + + self.minter_exists = false; + self.expected_max_total = None; + self.expected_total_minted = 0; + + assert!( + self.initialize_mint_governor(Some("Init: InitializeMintGovernor")), + "mint governor initialization must succeed", + ); + assert!( + self.transfer_authority_to_governor( + self.payer.pubkey(), + Some("Init: TransferAuthorityToGovernor"), + ), + "initial authority transfer must succeed", + ); + + self.assert_global_invariants(); + } + + #[flow] + fn flow_manage_minter(&mut self) { + self.manage_minter_flow("Flow: ManageMinter"); + } + + #[flow] + fn flow_mint(&mut self) { + self.mint_flow("Flow: Mint"); + } + + #[end] + fn end(&mut self) { + self.assert_global_invariants(); + } +} + +fn main() { + FuzzTest::fuzz(FUZZ_ITERATIONS.into(), FLOWS_PER_ITERATION.into()); +} diff --git a/trident-tests/fuzz_pbpp/fuzz_accounts.rs b/trident-tests/fuzz_pbpp/fuzz_accounts.rs new file mode 100644 index 000000000..8eedfd349 --- /dev/null +++ b/trident-tests/fuzz_pbpp/fuzz_accounts.rs @@ -0,0 +1,31 @@ +#![allow(non_snake_case)] +use trident_fuzz::fuzzing::*; + +/// Storage for all account addresses used in fuzz testing. +/// +/// This struct serves as a centralized repository for account addresses, +/// enabling their reuse across different instruction flows and test scenarios. +/// +/// Docs: https://ackee.xyz/trident/docs/latest/trident-api-macro/trident-types/fuzz-accounts/ +#[derive(Default)] +pub struct AccountAddresses { + pub performancePackage: AddressStorage, + + pub createKey: AddressStorage, + + pub tokenMint: AddressStorage, + + pub grantorTokenAccount: AddressStorage, + + pub grantor: AddressStorage, + + pub performancePackageTokenVault: AddressStorage, + + pub oracleAccount: AddressStorage, + + pub recipient: AddressStorage, + + pub recipientTokenAccount: AddressStorage, + + pub currentAuthority: AddressStorage, +} diff --git a/trident-tests/fuzz_pbpp/invariants/change_performance_package_authority.rs b/trident-tests/fuzz_pbpp/invariants/change_performance_package_authority.rs new file mode 100644 index 000000000..7654c5b6c --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/change_performance_package_authority.rs @@ -0,0 +1,91 @@ +use crate::common::types::price_based_performance_package::ChangePerformancePackageAuthorityParams; +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_change_performance_package_authority_invariants( + &mut self, + performance_package: Pubkey, + current_authority: Pubkey, + args: &ChangePerformancePackageAuthorityParams, + pre_pp: &PerformancePackage, + ) { + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after ChangePerformancePackageAuthority"); + + // Invariant 1: the signer passed as `current_authority` must equal the authority that was + // stored before the instruction executed (otherwise the instruction should not succeed). + assert_eq!( + current_authority, pre_pp.performancePackageAuthority, + "current_authority must match the pre-state performancePackageAuthority" + ); + + // Invariant 2: `performancePackageAuthority` must be updated exactly to the new authority + // provided in args. + assert_eq!( + post_pp.performancePackageAuthority, args.newPerformancePackageAuthority, + "performancePackageAuthority must be updated to args.newPerformancePackageAuthority" + ); + + // Invariant 3: `seqNum` must increment by exactly 1. + assert_eq!( + post_pp.seqNum, + pre_pp + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1" + ); + + // Invariant 4: no other fields should change as a result of changing authority. + assert_eq!( + post_pp.createKey, pre_pp.createKey, + "createKey must not change" + ); + assert_eq!(post_pp.pdaBump, pre_pp.pdaBump, "pdaBump must not change"); + assert_eq!( + post_pp.tokenMint, pre_pp.tokenMint, + "tokenMint must not change" + ); + assert_eq!( + post_pp.performancePackageTokenVault, pre_pp.performancePackageTokenVault, + "performancePackageTokenVault must not change" + ); + assert_eq!( + post_pp.recipient, pre_pp.recipient, + "recipient must not change" + ); + assert_eq!( + post_pp.minUnlockTimestamp, pre_pp.minUnlockTimestamp, + "minUnlockTimestamp must not change" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, pre_pp.oracleConfig.oracleAccount, + "oracleConfig.oracleAccount must not change" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, pre_pp.oracleConfig.byteOffset, + "oracleConfig.byteOffset must not change" + ); + assert_eq!( + post_pp.twapLengthSeconds, pre_pp.twapLengthSeconds, + "twapLengthSeconds must not change" + ); + assert_eq!( + post_pp.totalTokenAmount, pre_pp.totalTokenAmount, + "totalTokenAmount must not change" + ); + assert_eq!( + post_pp.alreadyUnlockedAmount, pre_pp.alreadyUnlockedAmount, + "alreadyUnlockedAmount must not change" + ); + assert_eq!( + post_pp.tranches.len(), + pre_pp.tranches.len(), + "tranches length must not change" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/invariants/complete_unlock.rs b/trident-tests/fuzz_pbpp/invariants/complete_unlock.rs new file mode 100644 index 000000000..3b587325a --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/complete_unlock.rs @@ -0,0 +1,224 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::PerformancePackageState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + pub fn verify_complete_unlock_invariants( + &mut self, + performance_package: Pubkey, + oracle_account: Pubkey, + performance_package_token_vault: Pubkey, + token_mint: Pubkey, + recipient_token_account: Pubkey, + recipient: Pubkey, + pre_pp: &PerformancePackage, + pre_vault_amount: u64, + pre_recipient_amount: u64, + timestamp_before_tx: i64, + ) { + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after CompleteUnlock"); + + // Invariant 1: Pre-state must be Unlocking and recipient address must match. + let (start_aggregator, start_timestamp) = match &pre_pp.state { + PerformancePackageState::Unlocking { + startAggregator, + startTimestamp, + } => (*startAggregator, *startTimestamp), + _ => panic!("Pre-state must be Unlocking if CompleteUnlock succeeded"), + }; + assert_eq!( + recipient, pre_pp.recipient, + "token_recipient account must match PerformancePackage.recipient" + ); + assert_eq!( + performance_package_token_vault, pre_pp.performancePackageTokenVault, + "Passed vault must match PerformancePackage.performancePackageTokenVault" + ); + assert_eq!( + token_mint, pre_pp.tokenMint, + "Passed token_mint must match PerformancePackage.tokenMint" + ); + + // Read oracle bytes to recompute TWAP + let oracle_acc = self.trident.get_account(&oracle_account); + let oracle_data = oracle_acc.data(); + let offset = pre_pp.oracleConfig.byteOffset as usize; + assert!( + oracle_data.len() >= offset + 24, + "Oracle account must have at least offset+24 bytes" + ); + let current_aggregator = + u128::from_le_bytes(oracle_data[offset..offset + 16].try_into().unwrap()); + let last_updated_timestamp = + i64::from_le_bytes(oracle_data[offset + 16..offset + 24].try_into().unwrap()); + + let timestamp_after_tx = self.trident.get_current_timestamp(); + assert!( + timestamp_after_tx >= last_updated_timestamp, + "Clock must be >= oracle last_updated_timestamp if CompleteUnlock succeeded" + ); + + let time_passed = last_updated_timestamp - start_timestamp; + assert!( + time_passed >= pre_pp.twapLengthSeconds as i64, + "time_passed must be >= twapLengthSeconds if CompleteUnlock succeeded" + ); + assert!(time_passed > 0, "time_passed must be > 0 for TWAP division"); + + let aggregator_change = current_aggregator.saturating_sub(start_aggregator); + let twap_price = aggregator_change / (time_passed as u128); + + // Compute expected tokens_to_unlock and expected tranche unlock flags. + let mut expected_tokens_to_unlock: u64 = 0; + let mut expected_tranches = pre_pp.tranches.clone(); + for tranche in expected_tranches.iter_mut() { + if tranche.isUnlocked { + continue; + } + if twap_price >= tranche.priceThreshold { + expected_tokens_to_unlock = expected_tokens_to_unlock + .checked_add(tranche.tokenAmount) + .expect("tokens_to_unlock overflow should be impossible"); + tranche.isUnlocked = true; + } else { + break; + } + } + let expected_already_unlocked = pre_pp + .alreadyUnlockedAmount + .checked_add(expected_tokens_to_unlock) + .expect("alreadyUnlockedAmount overflow should be impossible"); + + // Invariant 2: Post-state must reset back to Locked and seqNum increments by exactly 1. + assert!( + matches!(post_pp.state, PerformancePackageState::Locked), + "Post-state must be Locked after CompleteUnlock" + ); + assert_eq!( + post_pp.seqNum, + pre_pp + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on CompleteUnlock" + ); + + // Invariant 3: alreadyUnlockedAmount and tranche unlock flags must reflect TWAP logic. + assert_eq!( + post_pp.alreadyUnlockedAmount, expected_already_unlocked, + "alreadyUnlockedAmount must increase by exactly tokens_to_unlock" + ); + assert_eq!( + post_pp.tranches.len(), + expected_tranches.len(), + "Tranche count must not change" + ); + for (i, (post_t, exp_t)) in post_pp + .tranches + .iter() + .zip(expected_tranches.iter()) + .enumerate() + { + assert_eq!( + post_t.isUnlocked, exp_t.isUnlocked, + "Tranche[{i}].isUnlocked must match expected unlock based on TWAP" + ); + assert_eq!( + post_t.priceThreshold, exp_t.priceThreshold, + "Tranche[{i}].priceThreshold must not change" + ); + assert_eq!( + post_t.tokenAmount, exp_t.tokenAmount, + "Tranche[{i}].tokenAmount must not change" + ); + } + + // Invariant 4: token accounting must match expected_tokens_to_unlock. + let post_vault = self + .trident + .get_token_account(performance_package_token_vault) + .expect("Vault must exist after CompleteUnlock") + .account; + let post_recipient = self + .trident + .get_token_account(recipient_token_account) + .expect("Recipient token account must exist after CompleteUnlock") + .account; + + assert_eq!( + post_vault.mint, token_mint, + "Vault mint must match token_mint" + ); + assert_eq!( + post_vault.owner, performance_package, + "Vault owner must be the performance_package PDA" + ); + assert_eq!( + post_recipient.mint, token_mint, + "Recipient token account mint must match token_mint" + ); + assert_eq!( + post_recipient.owner, recipient, + "Recipient token account owner must match recipient" + ); + + let vault_delta = pre_vault_amount + .checked_sub(post_vault.amount) + .expect("Vault amount must not increase during CompleteUnlock"); + let recipient_delta = post_recipient + .amount + .checked_sub(pre_recipient_amount) + .expect("Recipient amount must not decrease during CompleteUnlock"); + + assert_eq!( + vault_delta, expected_tokens_to_unlock, + "Vault must decrease by exactly tokens_to_unlock" + ); + assert_eq!( + recipient_delta, expected_tokens_to_unlock, + "Recipient token account must increase by exactly tokens_to_unlock" + ); + + // Invariant 5: global accounting must remain consistent. + assert!( + post_pp.alreadyUnlockedAmount <= post_pp.totalTokenAmount, + "alreadyUnlockedAmount must not exceed totalTokenAmount" + ); + + // Invariant 6: CompleteUnlock must not mutate unrelated fields. + assert_eq!( + post_pp.recipient, pre_pp.recipient, + "recipient must not change" + ); + assert_eq!( + post_pp.performancePackageAuthority, pre_pp.performancePackageAuthority, + "performancePackageAuthority must not change" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, pre_pp.oracleConfig.oracleAccount, + "oracleConfig.oracleAccount must not change" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, pre_pp.oracleConfig.byteOffset, + "oracleConfig.byteOffset must not change" + ); + assert_eq!( + post_pp.twapLengthSeconds, pre_pp.twapLengthSeconds, + "twapLengthSeconds must not change" + ); + assert_eq!( + post_pp.totalTokenAmount, pre_pp.totalTokenAmount, + "totalTokenAmount must not change" + ); + assert!( + timestamp_before_tx <= timestamp_after_tx, + "Timestamp must not go backwards" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/invariants/execute_change.rs b/trident-tests/fuzz_pbpp/invariants/execute_change.rs new file mode 100644 index 000000000..681ccef3f --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/execute_change.rs @@ -0,0 +1,97 @@ +use crate::common::types::price_based_performance_package::ChangeRequest; +use crate::common::types::price_based_performance_package::ChangeType; +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::ProposerType; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_execute_change_invariants( + &mut self, + change_request: Pubkey, + performance_package: Pubkey, + executor: Pubkey, + pre_pp: &PerformancePackage, + pre_cr: &ChangeRequest, + ) { + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after ExecuteChange"); + + // Invariant 1: executor must be the opposite party of the proposerType stored in ChangeRequest. + match pre_cr.proposerType { + ProposerType::Recipient => { + assert_eq!( + executor, pre_pp.performancePackageAuthority, + "If recipient proposed, authority must execute" + ); + } + ProposerType::Authority => { + assert_eq!( + executor, pre_pp.recipient, + "If authority proposed, recipient must execute" + ); + } + } + + // Invariant 2: seqNum must increment by exactly 1. + assert_eq!( + post_pp.seqNum, + pre_pp + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "PerformancePackage.seqNum must increment by exactly 1 on ExecuteChange" + ); + + // Invariant 3: the change must be applied exactly as specified in ChangeRequest.changeType + // and no other changeable field may be modified (besides seqNum). + match &pre_cr.changeType { + ChangeType::Oracle { newOracleConfig } => { + assert_eq!( + post_pp.oracleConfig.oracleAccount, newOracleConfig.oracleAccount, + "ExecuteChange(Oracle) must update oracleConfig.oracleAccount" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, newOracleConfig.byteOffset, + "ExecuteChange(Oracle) must update oracleConfig.byteOffset" + ); + assert_eq!( + post_pp.recipient, pre_pp.recipient, + "ExecuteChange(Oracle) must not change recipient" + ); + } + ChangeType::Recipient { newRecipient } => { + assert_eq!( + post_pp.recipient, *newRecipient, + "ExecuteChange(Recipient) must update recipient" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, pre_pp.oracleConfig.oracleAccount, + "ExecuteChange(Recipient) must not change oracleConfig.oracleAccount" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, pre_pp.oracleConfig.byteOffset, + "ExecuteChange(Recipient) must not change oracleConfig.byteOffset" + ); + } + } + + // Invariant 4: performancePackageAuthority must not change on ExecuteChange. + assert_eq!( + post_pp.performancePackageAuthority, pre_pp.performancePackageAuthority, + "ExecuteChange must not change performancePackageAuthority" + ); + + // Invariant 5: the ChangeRequest account must be closed after ExecuteChange (close = executor). + // We treat non-existence as success here (Anchor close sets account to uninitialized). + let cr_after = self + .trident + .get_account_with_type::(&change_request, Some(8)); + assert!( + cr_after.is_none(), + "ChangeRequest must be closed (not readable) after ExecuteChange" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/invariants/initialize_performance_package.rs b/trident-tests/fuzz_pbpp/invariants/initialize_performance_package.rs new file mode 100644 index 000000000..55f1b867f --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/initialize_performance_package.rs @@ -0,0 +1,199 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::constants::PERFORMANCE_PACKAGE_SEED_PREFIX; +use crate::common::constants::TOKEN_PROGRAM_ID; +use crate::common::types::price_based_performance_package; +use crate::common::types::price_based_performance_package::InitializePerformancePackageParams; +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::PerformancePackageState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_initialize_performance_package_invariants( + &mut self, + performance_package: Pubkey, + create_key: Pubkey, + token_mint: Pubkey, + grantor_token_account: Pubkey, + grantor: Pubkey, + performance_package_token_vault: Pubkey, + args: &InitializePerformancePackageParams, + initial_grantor_token_amount: u64, + initial_vault_token_amount: u64, + timestamp_before_tx: i64, + ) { + let pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage account must exist after initialization"); + + // Invariant 1: `performance_package` address, stored `createKey`, and stored `pdaBump` + // must match PDA derivation from seeds used by the program. + let (expected_pda, expected_bump) = self.trident.find_program_address( + &[PERFORMANCE_PACKAGE_SEED_PREFIX, create_key.as_ref()], + &price_based_performance_package::program_id(), + ); + assert_eq!( + performance_package, expected_pda, + "PerformancePackage PDA must match seeds (performance_package, create_key)" + ); + assert_eq!( + pp.createKey, create_key, + "Stored createKey must match the signer used to derive the PDA" + ); + assert_eq!( + pp.pdaBump, expected_bump, + "Stored pdaBump must match PDA derivation bump" + ); + + // Invariant 2: all static fields persisted into `PerformancePackage` must match the + // instruction inputs and the expected initial state. + assert_eq!( + pp.minUnlockTimestamp, args.minUnlockTimestamp, + "Stored minUnlockTimestamp must match args" + ); + assert_eq!( + pp.twapLengthSeconds, args.twapLengthSeconds, + "Stored twapLengthSeconds must match args" + ); + assert_eq!( + pp.oracleConfig.oracleAccount, args.oracleConfig.oracleAccount, + "Stored oracleConfig.oracleAccount must match args" + ); + assert_eq!( + pp.oracleConfig.byteOffset, args.oracleConfig.byteOffset, + "Stored oracleConfig.byteOffset must match args" + ); + assert_eq!( + pp.recipient, args.grantee, + "Stored recipient must match args.grantee" + ); + assert_eq!( + pp.performancePackageAuthority, args.performancePackageAuthority, + "Stored performancePackageAuthority must match args" + ); + assert_eq!( + pp.tokenMint, token_mint, + "Stored tokenMint must match the token_mint account" + ); + assert_eq!( + pp.performancePackageTokenVault, performance_package_token_vault, + "Stored performancePackageTokenVault must match the provided vault account" + ); + assert_eq!( + pp.alreadyUnlockedAmount, 0, + "alreadyUnlockedAmount must start at 0" + ); + assert_eq!(pp.seqNum, 0, "seqNum must start at 0 on initialization"); + assert!( + matches!(pp.state, PerformancePackageState::Locked), + "PerformancePackage state must start as Locked" + ); + + // Invariant 3: tranches must be stored 1:1 with input tranches, and each stored tranche + // must start as locked (`isUnlocked=false`). Also, totalTokenAmount must equal the + // overflow-safe sum of tranche token amounts. + assert_eq!( + pp.tranches.len(), + args.tranches.len(), + "Stored tranche count must match args.tranches.len()" + ); + + let mut expected_total_u128: u128 = 0; + for (i, (stored, input)) in pp.tranches.iter().zip(args.tranches.iter()).enumerate() { + assert_eq!( + stored.priceThreshold, input.priceThreshold, + "Stored tranche[{i}].priceThreshold must match args" + ); + assert_eq!( + stored.tokenAmount, input.tokenAmount, + "Stored tranche[{i}].tokenAmount must match args" + ); + assert!( + !stored.isUnlocked, + "Stored tranche[{i}] must start locked (isUnlocked=false)" + ); + expected_total_u128 = expected_total_u128.saturating_add(input.tokenAmount as u128); + } + + assert!( + expected_total_u128 <= u64::MAX as u128, + "Sum of tranche token amounts must fit in u64 (no overflow)" + ); + let expected_total = expected_total_u128 as u64; + + assert_eq!( + pp.totalTokenAmount, expected_total, + "Stored totalTokenAmount must equal sum(args.tranches[*].tokenAmount)" + ); + + // Invariant 4: the token vault must be the ATA for (owner=performance_package, mint=token_mint) + // under the SPL Token program, and the on-chain state must point to that same vault. + let expected_ata = self.trident.get_associated_token_address( + &token_mint, + &performance_package, + &TOKEN_PROGRAM_ID, + ); + assert_eq!( + performance_package_token_vault, expected_ata, + "performance_package_token_vault must be the ATA of the performance_package PDA" + ); + + // Invariant 5: token accounting must be correct: + // - grantor_token_account decreases by totalTokenAmount + // - performance_package_token_vault increases by totalTokenAmount + // - vault mint/owner must be (token_mint, performance_package) + let final_grantor_acc = self + .trident + .get_token_account(grantor_token_account) + .expect("Grantor token account must exist after initialization") + .account; + let final_vault_acc = self + .trident + .get_token_account(performance_package_token_vault) + .expect("Performance package vault must exist after initialization") + .account; + + assert_eq!( + final_grantor_acc.mint, token_mint, + "Grantor token account mint must equal token_mint" + ); + assert_eq!( + final_grantor_acc.owner, grantor, + "Grantor token account owner must equal grantor" + ); + assert_eq!( + final_vault_acc.mint, token_mint, + "Vault mint must equal token_mint" + ); + assert_eq!( + final_vault_acc.owner, performance_package, + "Vault owner must be the performance_package PDA" + ); + + let grantor_spent = initial_grantor_token_amount + .checked_sub(final_grantor_acc.amount) + .expect("Grantor token account amount must not increase during initialization"); + let vault_received = final_vault_acc + .amount + .checked_sub(initial_vault_token_amount) + .expect("Vault token account amount must not decrease during initialization"); + + assert_eq!( + grantor_spent, expected_total, + "Grantor must spend exactly totalTokenAmount" + ); + assert_eq!( + vault_received, expected_total, + "Vault must receive exactly totalTokenAmount" + ); + + // Invariant 6: minUnlockTimestamp must remain strictly in the future relative to when the + // instruction executed (it was required by the program). + assert!( + pp.minUnlockTimestamp > timestamp_before_tx, + "minUnlockTimestamp must be > current time at initialization" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/invariants/mod.rs b/trident-tests/fuzz_pbpp/invariants/mod.rs new file mode 100644 index 000000000..400d3728c --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/mod.rs @@ -0,0 +1,6 @@ +pub mod change_performance_package_authority; +pub mod complete_unlock; +pub mod execute_change; +pub mod initialize_performance_package; +pub mod propose_change; +pub mod start_unlock; diff --git a/trident-tests/fuzz_pbpp/invariants/propose_change.rs b/trident-tests/fuzz_pbpp/invariants/propose_change.rs new file mode 100644 index 000000000..83802610f --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/propose_change.rs @@ -0,0 +1,142 @@ +use crate::common::constants::CHANGE_REQUEST_SEED_PREFIX; +use crate::common::types::price_based_performance_package; +use crate::common::types::price_based_performance_package::ChangeRequest; +use crate::common::types::price_based_performance_package::ChangeType; +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::ProposeChangeParams; +use crate::common::types::price_based_performance_package::ProposerType; +use crate::FuzzTest; +use trident_fuzz::fuzzing::Pubkey; + +impl FuzzTest { + pub fn verify_propose_change_invariants( + &mut self, + change_request: Pubkey, + performance_package: Pubkey, + proposer: Pubkey, + args: &ProposeChangeParams, + pre_pp: &PerformancePackage, + timestamp_before_tx: i64, + ) { + let post_cr = self + .trident + .get_account_with_type::(&change_request, Some(8)) + .expect("ChangeRequest must exist after ProposeChange"); + + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after ProposeChange"); + + // Invariant 1: `change_request` address must match PDA derivation from the program seeds. + let (expected_pda, expected_bump) = self.trident.find_program_address( + &[ + CHANGE_REQUEST_SEED_PREFIX, + performance_package.as_ref(), + proposer.as_ref(), + args.pdaNonce.to_le_bytes().as_ref(), + ], + &price_based_performance_package::program_id(), + ); + assert_eq!( + change_request, expected_pda, + "ChangeRequest PDA must match seeds (change_request, performance_package, proposer, pda_nonce)" + ); + assert_eq!( + post_cr.pdaBump, expected_bump, + "Stored pdaBump must match PDA derivation bump" + ); + + // Invariant 2: change_request must point to the correct performance_package. + assert_eq!( + post_cr.performancePackage, performance_package, + "ChangeRequest.performancePackage must equal the provided performance_package" + ); + + // Invariant 3: proposerType must reflect whether proposer is recipient or authority in the + // pre-state performance package. + let expected_proposer_type = if proposer == pre_pp.recipient { + ProposerType::Recipient + } else if proposer == pre_pp.performancePackageAuthority { + ProposerType::Authority + } else { + panic!("proposer must be recipient or performance package authority if tx succeeded"); + }; + match (&post_cr.proposerType, expected_proposer_type) { + (ProposerType::Recipient, ProposerType::Recipient) + | (ProposerType::Authority, ProposerType::Authority) => {} + _ => panic!("ChangeRequest.proposerType must match expected proposer type"), + } + + // Invariant 4: pdaNonce must be stored exactly as provided. + assert_eq!( + post_cr.pdaNonce, args.pdaNonce, + "ChangeRequest.pdaNonce must equal args.pdaNonce" + ); + + // Invariant 5: changeType stored in ChangeRequest must match args.changeType. + // (We compare variant + contained fields explicitly.) + match (&post_cr.changeType, &args.changeType) { + ( + ChangeType::Oracle { newOracleConfig: a }, + ChangeType::Oracle { newOracleConfig: b }, + ) => { + assert_eq!( + a.oracleAccount, b.oracleAccount, + "Oracle change must store newOracleConfig.oracleAccount correctly" + ); + assert_eq!( + a.byteOffset, b.byteOffset, + "Oracle change must store newOracleConfig.byteOffset correctly" + ); + } + ( + ChangeType::Recipient { newRecipient: a }, + ChangeType::Recipient { newRecipient: b }, + ) => { + assert_eq!(a, b, "Recipient change must store newRecipient correctly"); + } + _ => panic!("ChangeRequest.changeType must match args.changeType variant"), + } + + // Invariant 6: proposedAt must be within [timestamp_before_tx, now]. + let timestamp_after_tx = self.trident.get_current_timestamp(); + assert!( + post_cr.proposedAt >= timestamp_before_tx, + "proposedAt must be >= timestamp before tx" + ); + assert!( + post_cr.proposedAt <= timestamp_after_tx, + "proposedAt must be <= timestamp after tx" + ); + + // Invariant 7: ProposeChange must not mutate the PerformancePackage state + // (it only creates a ChangeRequest). + assert_eq!( + post_pp.recipient, pre_pp.recipient, + "PerformancePackage.recipient must not change on ProposeChange" + ); + assert_eq!( + post_pp.performancePackageAuthority, pre_pp.performancePackageAuthority, + "PerformancePackage.performancePackageAuthority must not change on ProposeChange" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, pre_pp.oracleConfig.oracleAccount, + "PerformancePackage.oracleConfig.oracleAccount must not change on ProposeChange" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, pre_pp.oracleConfig.byteOffset, + "PerformancePackage.oracleConfig.byteOffset must not change on ProposeChange" + ); + + // Invariant 8: ProposeChange increments seqNum by exactly 1. + assert_eq!( + post_pp.seqNum, + pre_pp + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "PerformancePackage.seqNum must increment by exactly 1 on ProposeChange" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/invariants/start_unlock.rs b/trident-tests/fuzz_pbpp/invariants/start_unlock.rs new file mode 100644 index 000000000..48569f187 --- /dev/null +++ b/trident-tests/fuzz_pbpp/invariants/start_unlock.rs @@ -0,0 +1,130 @@ +use crate::common::types::price_based_performance_package::PerformancePackage; +use crate::common::types::price_based_performance_package::PerformancePackageState; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +impl FuzzTest { + pub fn verify_start_unlock_invariants( + &mut self, + performance_package: Pubkey, + oracle_account: Pubkey, + recipient: Pubkey, + pre_pp: &PerformancePackage, + pre_vault_amount: u64, + ) { + let post_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist after StartUnlock"); + + // Read oracle data + let oracle_acc = self.trident.get_account(&oracle_account); + let oracle_data = oracle_acc.data(); + let offset = pre_pp.oracleConfig.byteOffset as usize; + + // Invariant 1: `recipient` must match the pre-state recipient (has_one constraint). + assert_eq!( + recipient, pre_pp.recipient, + "StartUnlock recipient signer must equal PerformancePackage.recipient" + ); + + // Invariant 2: StartUnlock must transition state from Locked -> Unlocking with + // {start_aggregator, start_timestamp} matching oracle bytes at configured offset. + assert!( + matches!(pre_pp.state, PerformancePackageState::Locked), + "Pre-state must be Locked if tx succeeded" + ); + assert!( + oracle_data.len() >= offset + 24, + "Oracle account must have at least offset+24 bytes" + ); + + let expected_start_aggregator = + u128::from_le_bytes(oracle_data[offset..offset + 16].try_into().unwrap()); + let expected_last_updated_timestamp = + i64::from_le_bytes(oracle_data[offset + 16..offset + 24].try_into().unwrap()); + + match &post_pp.state { + PerformancePackageState::Unlocking { + startAggregator, + startTimestamp, + } => { + assert_eq!( + *startAggregator, expected_start_aggregator, + "Unlocking.startAggregator must equal oracle aggregator" + ); + assert_eq!( + *startTimestamp, expected_last_updated_timestamp, + "Unlocking.startTimestamp must equal oracle last_updated_timestamp" + ); + } + _ => panic!("Post-state must be Unlocking after StartUnlock"), + } + + // Invariant 3: seqNum must increment by exactly 1. + assert_eq!( + post_pp.seqNum, + pre_pp + .seqNum + .checked_add(1) + .expect("seqNum overflow should be impossible"), + "seqNum must increment by exactly 1 on StartUnlock" + ); + + // Invariant 4: start timestamp must be within [minUnlockTimestamp, now] and unlock must be allowed. + let timestamp_after_tx = self.trident.get_current_timestamp(); + assert!( + timestamp_after_tx >= pre_pp.minUnlockTimestamp, + "Clock must be >= minUnlockTimestamp if StartUnlock succeeded" + ); + assert!( + expected_last_updated_timestamp >= pre_pp.minUnlockTimestamp, + "Oracle last_updated_timestamp must be >= minUnlockTimestamp if StartUnlock succeeded" + ); + assert!( + expected_last_updated_timestamp <= timestamp_after_tx, + "Oracle last_updated_timestamp must be <= current time if StartUnlock succeeded" + ); + + // Invariant 5: StartUnlock must not move tokens or mutate unrelated fields. + let post_vault_amount = self + .trident + .get_token_account(pre_pp.performancePackageTokenVault) + .expect("Vault must exist") + .account + .amount; + assert_eq!( + post_vault_amount, pre_vault_amount, + "Vault token amount must not change on StartUnlock" + ); + + assert_eq!( + post_pp.recipient, pre_pp.recipient, + "recipient must not change" + ); + assert_eq!( + post_pp.performancePackageAuthority, pre_pp.performancePackageAuthority, + "performancePackageAuthority must not change" + ); + assert_eq!( + post_pp.oracleConfig.oracleAccount, pre_pp.oracleConfig.oracleAccount, + "oracleConfig.oracleAccount must not change" + ); + assert_eq!( + post_pp.oracleConfig.byteOffset, pre_pp.oracleConfig.byteOffset, + "oracleConfig.byteOffset must not change" + ); + assert_eq!( + post_pp.twapLengthSeconds, pre_pp.twapLengthSeconds, + "twapLengthSeconds must not change" + ); + assert_eq!( + post_pp.totalTokenAmount, pre_pp.totalTokenAmount, + "totalTokenAmount must not change" + ); + assert_eq!( + post_pp.alreadyUnlockedAmount, pre_pp.alreadyUnlockedAmount, + "alreadyUnlockedAmount must not change on StartUnlock" + ); + } +} diff --git a/trident-tests/fuzz_pbpp/methods/mod.rs b/trident-tests/fuzz_pbpp/methods/mod.rs new file mode 100644 index 000000000..d71a37a60 --- /dev/null +++ b/trident-tests/fuzz_pbpp/methods/mod.rs @@ -0,0 +1,147 @@ +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::token::initialize_associated_token_account; +use crate::common::token::initialize_mint; +use crate::common::token::mint_to; +use crate::common::types::price_based_performance_package::InitializePerformancePackageParams; +pub mod pbpp; + +impl FuzzTest { + pub fn initial_setup(&mut self) { + self.trident + .airdrop(&self.payer.pubkey(), 500 * LAMPORTS_PER_SOL); + } + + /// Creates/overwrites a mock oracle account with the layout expected by our PBPP oracle config: + /// - `u128` aggregator (LE) at `byte_offset` + /// - `i64` last_updated_timestamp (LE) immediately after + pub fn upsert_mock_oracle_u128_i64( + &mut self, + oracle: Pubkey, + owner: Pubkey, + byte_offset: usize, + aggregator: u128, + last_updated_timestamp: i64, + ) { + let data_len = byte_offset + .checked_add(16 + 8) + .expect("oracle data length overflow"); + let mut oracle_data = vec![0u8; data_len]; + oracle_data[byte_offset..byte_offset + 16].copy_from_slice(&aggregator.to_le_bytes()); + oracle_data[byte_offset + 16..byte_offset + 24] + .copy_from_slice(&last_updated_timestamp.to_le_bytes()); + + let rent = self.trident.get_sysvar::(); + let lamports = rent.minimum_balance(oracle_data.len()); + let mut oracle_account_state = AccountSharedData::new(lamports, oracle_data.len(), &owner); + oracle_account_state.set_data_from_slice(&oracle_data); + self.trident + .set_account_custom(&oracle, &oracle_account_state); + } + + pub fn setup_mint(&mut self) -> Pubkey { + let mint = self.trident.random_keypair(); + initialize_mint( + &mut self.trident, + self.payer.pubkey(), + mint.pubkey(), + 6, + self.payer.pubkey(), + None, + None, + ); + mint.pubkey() + } + + pub fn setup_grantor_accounts(&mut self, token_mint: Pubkey) -> (Pubkey, Pubkey) { + let grantor = self.trident.random_keypair(); + let grantor_ata = initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + token_mint, + grantor.pubkey(), + ); + mint_to( + &mut self.trident, + grantor_ata, + token_mint, + self.payer.pubkey(), + 1_000_000_000_000, + ); + (grantor.pubkey(), grantor_ata) + } + + pub fn random_initialize_performance_package_params( + &mut self, + current_timestamp: i64, + oracle_account: Pubkey, + oracle_byte_offset: u32, + grantee: Pubkey, + performance_package_authority: Pubkey, + ) -> InitializePerformancePackageParams { + use crate::common::types::price_based_performance_package::InitializePerformancePackageParams; + use crate::common::types::price_based_performance_package::OracleConfig; + use crate::common::types::price_based_performance_package::Tranche; + + // 99% valid, 1% invalid + let mostly_valid = self.trident.random_from_range(1u8..=100u8) != 1; + + // Tranches: valid is 1..=10 (i.e., < 11) + let tranche_count: usize = if mostly_valid { + self.trident.random_from_range(1u32..=10u32) as usize + } else { + match self.trident.random_from_range(0u8..=2u8) { + 0 => 0, + 1 => 11 + self.trident.random_from_range(0u32..=5u32) as usize, + _ => self.trident.random_from_range(1u32..=10u32) as usize, + } + }; + + let mut tranches = Vec::with_capacity(tranche_count); + let mut threshold: u128 = 0; + for _ in 0..tranche_count { + threshold = threshold + .saturating_add(self.trident.random_from_range(0u64..=1_000_000_u64) as u128) + .saturating_add(1); + let token_amount = self.trident.random_from_range(1u64..=10_000_000u64); + tranches.push(Tranche::new(threshold, token_amount)); + } + + // minUnlockTimestamp: valid is in the future relative to init time. + // Keep it within ~1 hour so the later `forward_in_time(1h)` tends to satisfy StartUnlock. + let min_unlock_timestamp = if mostly_valid { + current_timestamp + self.trident.random_from_range(1i64..=60 * 60) + } else { + match self.trident.random_from_range(0u8..=1u8) { + 0 => current_timestamp, // not in future + _ => current_timestamp - 1, // in the past + } + }; + + // twapLengthSeconds: valid is [1 day, 1 year] + const DAY: u32 = 60 * 60 * 24; + const YEAR: u32 = 60 * 60 * 24 * 365; + let twap_length_seconds = if mostly_valid { + self.trident.random_from_range(DAY..=YEAR) + } else { + match self.trident.random_from_range(0u8..=4u8) { + 0 => 0, + 1 => 1, + 2 => DAY - 1, + 3 => YEAR + 1, + _ => u32::MAX, + } + }; + + let oracle_config = OracleConfig::new(oracle_account, oracle_byte_offset); + InitializePerformancePackageParams::new( + tranches, + min_unlock_timestamp, + oracle_config, + twap_length_seconds, + grantee, + performance_package_authority, + ) + } +} diff --git a/trident-tests/fuzz_pbpp/methods/pbpp.rs b/trident-tests/fuzz_pbpp/methods/pbpp.rs new file mode 100644 index 000000000..6116bdd97 --- /dev/null +++ b/trident-tests/fuzz_pbpp/methods/pbpp.rs @@ -0,0 +1,387 @@ +#![allow(clippy::too_many_arguments)] + +use crate::common::types::price_based_performance_package; +use crate::common::types::price_based_performance_package::*; +use crate::FuzzTest; +use trident_fuzz::fuzzing::*; + +use crate::common::constants::*; +use crate::common::pda::get_event_authority_pda; + +impl FuzzTest { + pub fn initialize_performance_package( + &mut self, + payer: Pubkey, + performance_package: Pubkey, + create_key: Pubkey, + token_mint: Pubkey, + grantor_token_account: Pubkey, + grantor: Pubkey, + performance_package_token_vault: Pubkey, + args: InitializePerformancePackageParams, + message: Option<&str>, + ) { + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Capture pre-state for invariants + let args_for_invariants = args.clone(); + let timestamp_before_tx = self.trident.get_current_timestamp(); + let initial_grantor_token_amount = self + .trident + .get_token_account(grantor_token_account) + .expect("Grantor token account must exist before initialization") + .account + .amount; + let initial_vault_token_amount = self + .trident + .get_token_account(performance_package_token_vault) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + // Setup instruction + let init_performance_package = price_based_performance_package::InitializePerformancePackageInstruction::data( + price_based_performance_package::InitializePerformancePackageInstructionData::new( + args, + ), + ) + .accounts(price_based_performance_package::InitializePerformancePackageInstructionAccounts::new( + performance_package, + create_key, + token_mint, + grantor_token_account, + grantor, + performance_package_token_vault, + payer, + SOLANA_PROGRAM_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + event_authority, + price_based_performance_package::program_id(), + )) + .instruction(); + + let res = self + .trident + .process_transaction(&[init_performance_package], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_initialize_performance_package_invariants( + performance_package, + create_key, + token_mint, + grantor_token_account, + grantor, + performance_package_token_vault, + &args_for_invariants, + initial_grantor_token_amount, + initial_vault_token_amount, + timestamp_before_tx, + ); + } + + pub fn change_performance_package_authority( + &mut self, + performance_package: Pubkey, + current_authority: Pubkey, + args: ChangePerformancePackageAuthorityParams, + message: Option<&str>, + ) { + // Capture pre-state for invariants + let args_for_invariants = args.clone(); + let pre_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist before ChangePerformancePackageAuthority"); + + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Setup instruction + let change_performance_package_authority = price_based_performance_package::ChangePerformancePackageAuthorityInstruction::data( + price_based_performance_package::ChangePerformancePackageAuthorityInstructionData::new( + args, + ), + ) + .accounts(price_based_performance_package::ChangePerformancePackageAuthorityInstructionAccounts::new( + performance_package, + current_authority, + event_authority, + price_based_performance_package::program_id(), + )) + .instruction(); + + let res = self + .trident + .process_transaction(&[change_performance_package_authority], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_change_performance_package_authority_invariants( + performance_package, + current_authority, + &args_for_invariants, + &pre_pp, + ); + } + + pub fn propose_change( + &mut self, + payer: Pubkey, + change_request: Pubkey, + performance_package: Pubkey, + proposer: Pubkey, + args: ProposeChangeParams, + message: Option<&str>, + ) { + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Capture pre-state for invariants + let args_for_invariants = args.clone(); + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist before ProposeChange"); + + // Setup instruction + let propose_change = price_based_performance_package::ProposeChangeInstruction::data( + price_based_performance_package::ProposeChangeInstructionData::new(args), + ) + .accounts( + price_based_performance_package::ProposeChangeInstructionAccounts::new( + change_request, + performance_package, + proposer, + payer, + SOLANA_PROGRAM_ID, + event_authority, + price_based_performance_package::program_id(), + ), + ) + .instruction(); + + let res = self.trident.process_transaction(&[propose_change], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_propose_change_invariants( + change_request, + performance_package, + proposer, + &args_for_invariants, + &pre_pp, + timestamp_before_tx, + ); + } + + pub fn execute_change( + &mut self, + change_request: Pubkey, + performance_package: Pubkey, + executor: Pubkey, + message: Option<&str>, + ) { + // Capture pre-state for invariants + let pre_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)); + if pre_pp.is_none() { + return; + } + let pre_pp = pre_pp.unwrap(); + let pre_cr = self + .trident + .get_account_with_type::(&change_request, Some(8)); + if pre_cr.is_none() { + return; + } + let pre_cr = pre_cr.unwrap(); + + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Setup instruction + let execute_change = price_based_performance_package::ExecuteChangeInstruction::data( + price_based_performance_package::ExecuteChangeInstructionData::new(), + ) + .accounts( + price_based_performance_package::ExecuteChangeInstructionAccounts::new( + change_request, + performance_package, + executor, + event_authority, + price_based_performance_package::program_id(), + ), + ) + .instruction(); + + let res = self.trident.process_transaction(&[execute_change], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_execute_change_invariants( + change_request, + performance_package, + executor, + &pre_pp, + &pre_cr, + ); + } + + pub fn start_unlock( + &mut self, + recipient: Pubkey, + performance_package: Pubkey, + oracle_account: Pubkey, + message: Option<&str>, + ) { + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Capture pre-state for invariants + let pre_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist before StartUnlock"); + let pre_vault_amount = self + .trident + .get_token_account(pre_pp.performancePackageTokenVault) + .expect("Vault must exist before StartUnlock") + .account + .amount; + + // Setup instruction + let start_unlock = price_based_performance_package::StartUnlockInstruction::data( + price_based_performance_package::StartUnlockInstructionData::new(), + ) + .accounts( + price_based_performance_package::StartUnlockInstructionAccounts::new( + performance_package, + oracle_account, + recipient, + event_authority, + price_based_performance_package::program_id(), + ), + ) + .instruction(); + + let res = self.trident.process_transaction(&[start_unlock], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_start_unlock_invariants( + performance_package, + oracle_account, + recipient, + &pre_pp, + pre_vault_amount, + ); + } + + pub fn complete_unlock( + &mut self, + performance_package: Pubkey, + oracle_account: Pubkey, + performance_package_token_vault: Pubkey, + token_mint: Pubkey, + recipient_token_account: Pubkey, + recipient: Pubkey, + payer: Pubkey, + message: Option<&str>, + ) { + let event_authority = get_event_authority_pda( + &mut self.trident, + price_based_performance_package::program_id(), + ); + + // Capture pre-state for invariants + let timestamp_before_tx = self.trident.get_current_timestamp(); + let pre_pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist before CompleteUnlock"); + let pre_vault_amount = self + .trident + .get_token_account(performance_package_token_vault) + .expect("Vault must exist before CompleteUnlock") + .account + .amount; + let pre_recipient_amount = self + .trident + .get_token_account(recipient_token_account) + .map(|acc| acc.account.amount) + .unwrap_or(0); + + // Setup instruction + let complete_unlock = price_based_performance_package::CompleteUnlockInstruction::data( + price_based_performance_package::CompleteUnlockInstructionData::new(), + ) + .accounts( + price_based_performance_package::CompleteUnlockInstructionAccounts::new( + performance_package, + oracle_account, + performance_package_token_vault, + token_mint, + recipient_token_account, + recipient, + payer, + SOLANA_PROGRAM_ID, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + event_authority, + price_based_performance_package::program_id(), + ), + ) + .instruction(); + + let res = self + .trident + .process_transaction(&[complete_unlock], message); + + if !res.is_success() { + return; + } + + // Verify invariants + self.verify_complete_unlock_invariants( + performance_package, + oracle_account, + performance_package_token_vault, + token_mint, + recipient_token_account, + recipient, + &pre_pp, + pre_vault_amount, + pre_recipient_amount, + timestamp_before_tx, + ); + } +} diff --git a/trident-tests/fuzz_pbpp/test_fuzz.rs b/trident-tests/fuzz_pbpp/test_fuzz.rs new file mode 100644 index 000000000..4f11163ca --- /dev/null +++ b/trident-tests/fuzz_pbpp/test_fuzz.rs @@ -0,0 +1,464 @@ +use fuzz_accounts::*; +use trident_fuzz::fuzzing::*; +mod fuzz_accounts; +mod invariants; + +pub mod methods; + +#[path = "../common/mod.rs"] +pub mod common; +use crate::common::constants::*; +use crate::common::pda::get_change_request_pda; +use crate::common::pda::get_performance_package_pda; +use crate::common::token::initialize_associated_token_account; +use crate::common::types::price_based_performance_package::*; +#[derive(FuzzTestMethods)] +struct FuzzTest { + /// Trident client for interacting with the Solana program + trident: Trident, + /// Storage for all account addresses used in fuzz testing + fuzz_accounts: AccountAddresses, + + // ============================================================================ + // Accounts + payer: Keypair, + // ============================================================================ +} + +#[flow_executor] +impl FuzzTest { + fn new() -> Self { + let mut trident = Trident::default(); + + let payer = trident.random_keypair(); + + Self { + trident, + fuzz_accounts: AccountAddresses::default(), + payer, + } + } + + #[init] + fn start(&mut self) { + self.initial_setup(); + + // Core accounts + let create_key = self.trident.random_keypair(); + let oracle_account = self.trident.random_keypair(); + let performance_package = + get_performance_package_pda(&mut self.trident, create_key.pubkey()); + + // Token mint + funding + let token_mint = self.setup_mint(); + let (grantor, grantor_token_account) = self.setup_grantor_accounts(token_mint); + let performance_package_token_vault = initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + token_mint, + performance_package, + ); + + // Two recipients + two authorities (we want 50% chance to pick the correct one in flows). + let recipient_a = self.trident.random_keypair().pubkey(); + let recipient_b = self.trident.random_keypair().pubkey(); + let authority_a = self.payer.pubkey(); + let authority_b = self.trident.random_keypair().pubkey(); + + // Seed ATAs for both recipients. + let recipient_a_token_account = initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + token_mint, + recipient_a, + ); + let recipient_b_token_account = initialize_associated_token_account( + &mut self.trident, + self.payer.pubkey(), + token_mint, + recipient_b, + ); + + // Persist addresses for flows + self.fuzz_accounts + .createKey + .insert_with_address(create_key.pubkey()); + self.fuzz_accounts + .performancePackage + .insert_with_address(performance_package); + self.fuzz_accounts + .oracleAccount + .insert_with_address(oracle_account.pubkey()); + self.fuzz_accounts.tokenMint.insert_with_address(token_mint); + self.fuzz_accounts.grantor.insert_with_address(grantor); + self.fuzz_accounts + .grantorTokenAccount + .insert_with_address(grantor_token_account); + self.fuzz_accounts + .performancePackageTokenVault + .insert_with_address(performance_package_token_vault); + self.fuzz_accounts + .recipient + .insert_with_address(recipient_a); + self.fuzz_accounts + .recipient + .insert_with_address(recipient_b); + self.fuzz_accounts + .recipientTokenAccount + .insert_with_address(recipient_a_token_account); + self.fuzz_accounts + .recipientTokenAccount + .insert_with_address(recipient_b_token_account); + self.fuzz_accounts + .currentAuthority + .insert_with_address(authority_a); + self.fuzz_accounts + .currentAuthority + .insert_with_address(authority_b); + + // Initialize performance package (mostly-valid, occasionally-invalid to test rejects) + let current_timestamp = self.trident.get_current_timestamp(); + let initialize_performance_package_args = self + .random_initialize_performance_package_params( + current_timestamp, + oracle_account.pubkey(), + 0, + recipient_a, + authority_a, + ); + + self.initialize_performance_package( + self.payer.pubkey(), + performance_package, + create_key.pubkey(), + token_mint, + grantor_token_account, + grantor, + performance_package_token_vault, + initialize_performance_package_args, + Some("Instruction: Initialize Performance Package"), + ); + } + + #[flow] + fn flow1_start_unlock(&mut self) { + let performance_package = self + .fuzz_accounts + .performancePackage + .get(&mut self.trident) + .expect("performancePackage must be set"); + + let pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)); + if pp.is_none() { + return; + } + let pp = pp.unwrap(); + + // If we're not locked, don't spam meaningless failures (but rarely still try). + if !matches!(pp.state, PerformancePackageState::Locked) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + // 50% correct recipient, 50% wrong (picked from our two known recipients) + let recipient = self + .fuzz_accounts + .recipient + .get(&mut self.trident) + .expect("recipient must be set"); + + // 99%: set up oracle + time so StartUnlock passes + let mostly_valid = self.trident.random_from_range(1u8..=100u8) != 1; + + if mostly_valid { + let now = self.trident.get_current_timestamp(); + if now < pp.minUnlockTimestamp { + self.trident + .forward_in_time((pp.minUnlockTimestamp - now) + 1); + } + let now = self.trident.get_current_timestamp(); + let start_agg = self.trident.random_from_range(1u64..=10_000_000u64) as u128; + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + start_agg, + now, + ); + } else { + // 1%: intentionally wrong setup + match self.trident.random_from_range(0u8..=2u8) { + 0 => { + // don't touch oracle at all => InvalidOracleData likely + } + 1 => { + // oracle timestamp too early + let bad_ts = pp.minUnlockTimestamp.saturating_sub(1); + let start_agg = self.trident.random_from_range(1u64..=10_000_000u64) as u128; + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + start_agg, + bad_ts, + ); + } + _ => { + // oracle timestamp in the future (clock < ts) + let now = self.trident.get_current_timestamp(); + let bad_ts = now.saturating_add(1); + let start_agg = self.trident.random_from_range(1u64..=10_000_000u64) as u128; + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + start_agg, + bad_ts, + ); + } + } + } + + self.start_unlock( + recipient, + performance_package, + pp.oracleConfig.oracleAccount, + Some("Flow1: StartUnlock"), + ); + } + + #[flow] + fn flow2_complete_unlock(&mut self) { + let performance_package = self + .fuzz_accounts + .performancePackage + .get(&mut self.trident) + .expect("performancePackage must be set"); + + let pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)); + if pp.is_none() { + return; + } + let pp = pp.unwrap(); + + // Only makes sense when Unlocking; otherwise return most of the time. + if !matches!(pp.state, PerformancePackageState::Unlocking { .. }) + && self.trident.random_from_range(1u8..=100u8) != 1 + { + return; + } + + let recipient = self + .fuzz_accounts + .recipient + .get(&mut self.trident) + .expect("recipient must be set"); + + // 99%: set up oracle + time so CompleteUnlock passes (modulo wrong token_recipient) + let mostly_valid = self.trident.random_from_range(1u8..=100u8) != 1; + + if let PerformancePackageState::Unlocking { + startAggregator, + startTimestamp, + } = pp.state + { + if mostly_valid { + let target_ts = startTimestamp + pp.twapLengthSeconds as i64 + 1; + let now = self.trident.get_current_timestamp(); + if now < target_ts { + self.trident.forward_in_time((target_ts - now) + 1); + } + + let now = self.trident.get_current_timestamp(); + let time_passed = now.saturating_sub(startTimestamp).max(1); + let max_threshold = pp + .tranches + .iter() + .map(|t| t.priceThreshold) + .max() + .unwrap_or(0); + + let end_agg = startAggregator.saturating_add( + (max_threshold.saturating_add(1)).saturating_mul(time_passed as u128), + ); + + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + end_agg, + now, + ); + } else { + // 1%: intentionally fail (too little time or too little price increase) + match self.trident.random_from_range(0u8..=1u8) { + 0 => { + let now = self.trident.get_current_timestamp(); + let target_ts = + startTimestamp + (pp.twapLengthSeconds as i64).saturating_sub(1); + if now < target_ts { + self.trident.forward_in_time((target_ts - now) + 1); + } + let now = self.trident.get_current_timestamp(); + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + startAggregator.saturating_add(1), + now, + ); + } + _ => { + let target_ts = startTimestamp + pp.twapLengthSeconds as i64 + 1; + let now = self.trident.get_current_timestamp(); + if now < target_ts { + self.trident.forward_in_time((target_ts - now) + 1); + } + let now = self.trident.get_current_timestamp(); + self.upsert_mock_oracle_u128_i64( + pp.oracleConfig.oracleAccount, + program_id(), + pp.oracleConfig.byteOffset as usize, + startAggregator.saturating_add(1), + now, + ); + } + } + } + } + + let recipient_token_account = + self.trident + .get_associated_token_address(&pp.tokenMint, &recipient, &TOKEN_PROGRAM_ID); + + self.complete_unlock( + performance_package, + pp.oracleConfig.oracleAccount, + pp.performancePackageTokenVault, + pp.tokenMint, + recipient_token_account, + recipient, + self.payer.pubkey(), + Some("Flow2: CompleteUnlock"), + ); + } + + #[flow] + fn flow3_admin_bundle(&mut self) { + let performance_package = self + .fuzz_accounts + .performancePackage + .get(&mut self.trident) + .expect("performancePackage must be set"); + + let pp = self + .trident + .get_account_with_type::(&performance_package, Some(8)); + if pp.is_none() { + return; + } + let pp = pp.unwrap(); + + // It is possible that random is same as the current authority or recipient. + let random_recipient = self + .fuzz_accounts + .recipient + .get(&mut self.trident) + .expect("recipient must be set"); + let random_authority = self + .fuzz_accounts + .currentAuthority + .get(&mut self.trident) + .expect("currentAuthority must be set"); + + // 1/3: change authority (switch between the two) + if self.trident.random_from_range(0u8..=2u8) == 0 { + let authority = if self.trident.random_from_range(0u8..10u8) != 0 { + random_authority + } else { + pp.performancePackageAuthority + }; + + self.change_performance_package_authority( + performance_package, + authority, + ChangePerformancePackageAuthorityParams::new(random_authority), + Some("Flow3: ChangePerformancePackageAuthority"), + ); + return; + } + + // Otherwise: propose + execute a change (recipient or oracle) + let proposer = if self.trident.random_from_range(0u8..=1u8) == 0 { + if self.trident.random_from_range(0u8..10u8) != 0 { + pp.recipient + } else { + random_recipient + } + } else if self.trident.random_from_range(0u8..10u8) != 0 { + pp.performancePackageAuthority + } else { + random_authority + }; + + let pda_nonce = self.trident.random_from_range(0u32..=u32::MAX); + let change_request = + get_change_request_pda(&mut self.trident, performance_package, proposer, pda_nonce); + + let change_type = if self.trident.random_from_range(0u8..=1u8) == 0 { + ChangeType::Recipient { + newRecipient: random_recipient, + } + } else { + let new_oracle = self.trident.random_keypair().pubkey(); + ChangeType::Oracle { + newOracleConfig: OracleConfig::new(new_oracle, pp.oracleConfig.byteOffset), + } + }; + + self.propose_change( + self.payer.pubkey(), + change_request, + performance_package, + proposer, + ProposeChangeParams::new(change_type, pda_nonce), + Some("Flow3: ProposeChange"), + ); + + let executor = if proposer == pp.recipient || proposer == random_recipient { + pp.performancePackageAuthority + } else { + pp.recipient + }; + + self.execute_change( + change_request, + performance_package, + executor, + Some("Flow3: ExecuteChange"), + ); + + // If the executed change updated the oracle config, keep fuzz_accounts in sync. + let pp_after = self + .trident + .get_account_with_type::(&performance_package, Some(8)) + .expect("PerformancePackage must exist"); + if pp_after.oracleConfig.oracleAccount != pp.oracleConfig.oracleAccount { + self.fuzz_accounts + .oracleAccount + .remove(&pp.oracleConfig.oracleAccount); + self.fuzz_accounts + .oracleAccount + .insert_with_address(pp_after.oracleConfig.oracleAccount); + } + } +} + +fn main() { + FuzzTest::fuzz(10000, 1000); +} diff --git a/trident-tests/programs/mainnet/meteora/damm_v2.so b/trident-tests/programs/mainnet/meteora/damm_v2.so new file mode 100644 index 000000000..e61dccf32 Binary files /dev/null and b/trident-tests/programs/mainnet/meteora/damm_v2.so differ diff --git a/trident-tests/programs/mainnet/squads/squads.so b/trident-tests/programs/mainnet/squads/squads.so new file mode 100644 index 000000000..017fdadcb Binary files /dev/null and b/trident-tests/programs/mainnet/squads/squads.so differ diff --git a/trident-tests/rust-toolchain.toml b/trident-tests/rust-toolchain.toml new file mode 100644 index 000000000..d72668b05 --- /dev/null +++ b/trident-tests/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.91.0"